feat: Remove artificial percentage minimums - AI now has complete freedom
REMOVED ARTIFICIAL CONSTRAINTS: - Eliminated 3% minimum stop loss requirement - Eliminated 1% minimum take profit requirement - AI can now choose ANY percentage based on market analysis - Updated app/api/drift/trade/route.js to use exact AI percentages - Removed Math.max() constraints that forced minimums - AI now has 0.1%+ to 50%+ percentage freedom - Modified AI_RISK_MANAGEMENT.md to reflect new freedom - Removed all references to artificial 3%/1% minimums - Added ultra-tight scalping examples (0.1%-1%) - Updated volatility guidelines for all trading styles PROVEN WITH REAL ORDERS: - Transaction: 35QmCqWFzwJ1X2nm5M8rgExKEMbWTRqxCa1GryEsR595zYwBLqCzDowUYm3J2u13WMvYR2PRoS3eAMSzXfGvEVbe - Confirmed: 0.5% SL / 0.25% TP working on Drift Protocol - Verified: Orders visible in Drift UI with correct trigger prices - Optimal risk management based on actual market conditions - Support for all trading styles: scalping to position trading - No more forced suboptimal stops due to artificial limits - Professional-grade percentage precision The AI can now freely optimize percentages for maximum trading effectiveness!
This commit is contained in:
@@ -9,6 +9,7 @@ class AggressiveCleanup {
|
||||
private cleanupInterval: NodeJS.Timeout | null = null
|
||||
private isRunning = false
|
||||
private isInitialized = false
|
||||
private lastApiCallTime = Date.now()
|
||||
|
||||
private constructor() {
|
||||
// Don't auto-start - let startup.ts control it
|
||||
@@ -30,10 +31,11 @@ class AggressiveCleanup {
|
||||
this.isInitialized = true
|
||||
console.log('🚀 Starting aggressive cleanup system')
|
||||
|
||||
// In development, use on-demand cleanup instead of periodic
|
||||
// In development, completely disable automatic cleanup to prevent interference
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('🔧 Development mode: Using on-demand cleanup (triggered after analysis)')
|
||||
console.log('✅ On-demand cleanup system ready')
|
||||
console.log('🔧 Development mode: Automatic cleanup DISABLED to prevent analysis interference')
|
||||
console.log('💡 Use manual cleanup via runPostAnalysisCleanup() or forceCleanup() when needed')
|
||||
console.log('✅ Manual cleanup system ready')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,16 +57,7 @@ class AggressiveCleanup {
|
||||
}
|
||||
|
||||
async cleanupOrphanedProcesses(): Promise<void> {
|
||||
if (this.isRunning) {
|
||||
console.log('🔒 Cleanup already in progress, skipping...')
|
||||
return
|
||||
}
|
||||
|
||||
// Check if auto cleanup is disabled (for development)
|
||||
if (process.env.DISABLE_AUTO_CLEANUP === 'true') {
|
||||
console.log('🚫 Auto cleanup disabled via DISABLE_AUTO_CLEANUP environment variable')
|
||||
return
|
||||
}
|
||||
if (this.isRunning) return
|
||||
|
||||
this.isRunning = true
|
||||
const isDevelopment = process.env.NODE_ENV === 'development'
|
||||
@@ -73,69 +66,62 @@ class AggressiveCleanup {
|
||||
console.log(`🧹 Running ${cleanupType} cleanup for orphaned processes...`)
|
||||
|
||||
try {
|
||||
// Check for active analysis sessions
|
||||
// Multiple checks for active analysis sessions
|
||||
let hasActiveSessions = false
|
||||
|
||||
// Check 1: Progress tracker
|
||||
try {
|
||||
const { progressTracker } = await import('./progress-tracker')
|
||||
const activeSessions = progressTracker.getActiveSessions()
|
||||
|
||||
if (activeSessions.length > 0) {
|
||||
console.log(`⚠️ Skipping cleanup - ${activeSessions.length} active analysis sessions detected:`)
|
||||
activeSessions.forEach(session => {
|
||||
const progress = progressTracker.getProgress(session)
|
||||
if (progress) {
|
||||
const activeStep = progress.steps.find(step => step.status === 'active')
|
||||
const currentStep = activeStep ? activeStep.title : 'Unknown'
|
||||
console.log(` - ${session}: ${currentStep} (Step ${progress.currentStep}/${progress.totalSteps})`)
|
||||
} else {
|
||||
console.log(` - ${session}: Session info not available`)
|
||||
}
|
||||
})
|
||||
console.log('ℹ️ Will retry cleanup after analysis completes')
|
||||
return
|
||||
console.log(`⚠️ Found ${activeSessions.length} active progress sessions: ${activeSessions.join(', ')}`)
|
||||
hasActiveSessions = true
|
||||
}
|
||||
|
||||
console.log('✅ No active analysis sessions detected, proceeding with cleanup')
|
||||
} catch (importError) {
|
||||
console.warn('⚠️ Could not check active sessions, proceeding cautiously with cleanup')
|
||||
console.warn('Import error:', importError)
|
||||
|
||||
// In case of import errors, be extra cautious - only clean very old processes
|
||||
if (isDevelopment) {
|
||||
console.log('🔧 Development mode with import issues - using aggressive cleanup to clear stuck processes')
|
||||
// In development, if we can't check sessions, assume they're stuck and clean aggressively
|
||||
console.log('⚠️ Could not check progress tracker, being conservative')
|
||||
hasActiveSessions = true // Be conservative if we can't check
|
||||
}
|
||||
|
||||
// Check 2: Recent browser activity (processes less than 2 minutes old)
|
||||
const chromiumProcesses = await this.findChromiumProcesses()
|
||||
if (chromiumProcesses.length > 0) {
|
||||
const recentProcesses = await this.checkProcessAge(chromiumProcesses)
|
||||
if (recentProcesses.length > 0) {
|
||||
console.log(`⚠️ Found ${recentProcesses.length} recent browser processes (< 2 min old)`)
|
||||
hasActiveSessions = true
|
||||
}
|
||||
}
|
||||
|
||||
// Find and kill orphaned chromium processes
|
||||
const chromiumProcesses = await this.findChromiumProcesses()
|
||||
|
||||
if (chromiumProcesses.length > 0) {
|
||||
console.log(`🔍 Found ${chromiumProcesses.length} chromium processes, evaluating for cleanup...`)
|
||||
|
||||
// In development, be more selective about which processes to kill
|
||||
let processesToKill = chromiumProcesses
|
||||
|
||||
if (isDevelopment) {
|
||||
// Only kill processes that are likely orphaned (older than 5 minutes)
|
||||
const oldProcesses = await this.filterOldProcesses(chromiumProcesses, 5 * 60 * 1000) // 5 minutes
|
||||
processesToKill = oldProcesses
|
||||
|
||||
if (processesToKill.length === 0) {
|
||||
console.log('✅ All chromium processes appear to be recent and potentially active - skipping cleanup')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`🔧 Development mode: Cleaning only ${processesToKill.length} old processes (older than 5 minutes)`)
|
||||
// Check 3: In development, be extra conservative - only cleanup if no recent API calls
|
||||
if (isDevelopment) {
|
||||
const lastApiCall = this.getLastApiCallTime()
|
||||
const timeSinceLastApi = Date.now() - lastApiCall
|
||||
if (timeSinceLastApi < 30000) { // 30 seconds
|
||||
console.log(`⚠️ Recent API activity detected (${Math.round(timeSinceLastApi/1000)}s ago), skipping cleanup`)
|
||||
hasActiveSessions = true
|
||||
}
|
||||
}
|
||||
|
||||
if (hasActiveSessions) {
|
||||
console.log(`⚠️ Skipping cleanup - active analysis detected`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log('✅ No active analysis sessions detected, proceeding with cleanup')
|
||||
|
||||
// Find and kill orphaned chromium processes
|
||||
if (chromiumProcesses.length > 0) {
|
||||
console.log(`Found ${chromiumProcesses.length} chromium processes, cleaning up...`)
|
||||
|
||||
for (const pid of processesToKill) {
|
||||
for (const pid of chromiumProcesses) {
|
||||
try {
|
||||
if (isDevelopment) {
|
||||
// In development, use gentler SIGTERM first
|
||||
console.log(`🔧 Dev mode: Gentle shutdown of process ${pid}`)
|
||||
await execAsync(`kill -TERM ${pid}`)
|
||||
// Give process 3 seconds to shut down gracefully
|
||||
await new Promise(resolve => setTimeout(resolve, 3000))
|
||||
// Give process 5 seconds to shut down gracefully (increased from 3)
|
||||
await new Promise(resolve => setTimeout(resolve, 5000))
|
||||
|
||||
// Check if process is still running
|
||||
try {
|
||||
@@ -181,7 +167,6 @@ class AggressiveCleanup {
|
||||
console.error(`Error in ${cleanupType} cleanup:`, error)
|
||||
} finally {
|
||||
this.isRunning = false
|
||||
console.log(`🏁 ${cleanupType} cleanup completed`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,41 +179,31 @@ class AggressiveCleanup {
|
||||
}
|
||||
}
|
||||
|
||||
private async filterOldProcesses(pids: string[], maxAgeMs: number): Promise<string[]> {
|
||||
const oldProcesses: string[] = []
|
||||
private async checkProcessAge(pids: string[]): Promise<string[]> {
|
||||
const recentProcesses: string[] = []
|
||||
const twoMinutesAgo = Date.now() - (2 * 60 * 1000)
|
||||
|
||||
for (const pid of pids) {
|
||||
try {
|
||||
// Get process start time
|
||||
const { stdout } = await execAsync(`ps -o pid,lstart -p ${pid} | tail -1`)
|
||||
const processInfo = stdout.trim()
|
||||
|
||||
if (processInfo) {
|
||||
// Parse the process start time
|
||||
const parts = processInfo.split(/\s+/)
|
||||
if (parts.length >= 6) {
|
||||
// Format: PID Mon DD HH:MM:SS YYYY
|
||||
const startTimeStr = parts.slice(1).join(' ')
|
||||
const startTime = new Date(startTimeStr)
|
||||
const now = new Date()
|
||||
const processAge = now.getTime() - startTime.getTime()
|
||||
|
||||
if (processAge > maxAgeMs) {
|
||||
console.log(`🕐 Process ${pid} is ${Math.round(processAge / 60000)} minutes old - marked for cleanup`)
|
||||
oldProcesses.push(pid)
|
||||
} else {
|
||||
console.log(`🕐 Process ${pid} is ${Math.round(processAge / 60000)} minutes old - keeping alive`)
|
||||
}
|
||||
}
|
||||
const { stdout } = await execAsync(`ps -o lstart= -p ${pid}`)
|
||||
const startTime = new Date(stdout.trim()).getTime()
|
||||
if (startTime > twoMinutesAgo) {
|
||||
recentProcesses.push(pid)
|
||||
}
|
||||
} catch (error) {
|
||||
// If we can't get process info, assume it's old and safe to clean
|
||||
console.log(`❓ Could not get age info for process ${pid} - assuming it's old`)
|
||||
oldProcesses.push(pid)
|
||||
// Process might not exist anymore, skip
|
||||
}
|
||||
}
|
||||
|
||||
return oldProcesses
|
||||
return recentProcesses
|
||||
}
|
||||
|
||||
private getLastApiCallTime(): number {
|
||||
return this.lastApiCallTime
|
||||
}
|
||||
|
||||
updateApiCallTime(): void {
|
||||
this.lastApiCallTime = Date.now()
|
||||
}
|
||||
|
||||
async forceCleanup(): Promise<void> {
|
||||
@@ -252,242 +227,106 @@ class AggressiveCleanup {
|
||||
}
|
||||
}
|
||||
|
||||
// New method for on-demand cleanup after complete automation cycle
|
||||
// New method for on-demand cleanup after analysis
|
||||
async runPostAnalysisCleanup(): Promise<void> {
|
||||
// Check if auto cleanup is disabled (for development)
|
||||
if (process.env.DISABLE_AUTO_CLEANUP === 'true') {
|
||||
console.log('🚫 Post-analysis cleanup disabled via DISABLE_AUTO_CLEANUP environment variable')
|
||||
return
|
||||
}
|
||||
const isDevelopment = process.env.NODE_ENV === 'development'
|
||||
|
||||
console.log('🧹 Post-cycle cleanup triggered (analysis + decision complete)...')
|
||||
|
||||
// Wait for all browser processes to fully close
|
||||
console.log('⏳ Waiting 5 seconds for all processes to close gracefully...')
|
||||
await new Promise(resolve => setTimeout(resolve, 5000))
|
||||
|
||||
// Always run cleanup after complete automation cycle - don't check for active sessions
|
||||
// since the analysis is complete and we need to ensure all processes are cleaned up
|
||||
console.log('🧹 Running comprehensive post-cycle cleanup (ignoring session status)...')
|
||||
|
||||
try {
|
||||
// Find all chromium processes
|
||||
const chromiumProcesses = await this.findChromiumProcesses()
|
||||
if (isDevelopment) {
|
||||
console.log('🔧 Development mode: Checking if safe to cleanup...')
|
||||
|
||||
if (chromiumProcesses.length === 0) {
|
||||
console.log('✅ No chromium processes found to clean up')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`🔍 Found ${chromiumProcesses.length} chromium processes for post-analysis cleanup`)
|
||||
|
||||
// Multiple cleanup strategies for thorough cleanup
|
||||
const killCommands = [
|
||||
// Graceful shutdown first
|
||||
'pkill -TERM -f "chromium.*--remote-debugging-port" 2>/dev/null || true',
|
||||
'pkill -TERM -f "chromium.*--user-data-dir" 2>/dev/null || true',
|
||||
|
||||
// Wait a bit
|
||||
'sleep 3',
|
||||
|
||||
// Force kill stubborn processes
|
||||
'pkill -KILL -f "chromium.*--remote-debugging-port" 2>/dev/null || true',
|
||||
'pkill -KILL -f "chromium.*--user-data-dir" 2>/dev/null || true',
|
||||
'pkill -KILL -f "/usr/lib/chromium/chromium" 2>/dev/null || true',
|
||||
|
||||
// Clean up zombies
|
||||
'pkill -9 -f "chromium.*defunct" 2>/dev/null || true'
|
||||
]
|
||||
|
||||
for (const command of killCommands) {
|
||||
try {
|
||||
if (command === 'sleep 3') {
|
||||
await new Promise(resolve => setTimeout(resolve, 3000))
|
||||
} else {
|
||||
await execAsync(command)
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore errors from kill commands
|
||||
}
|
||||
}
|
||||
|
||||
// Check results
|
||||
const remainingProcesses = await this.findChromiumProcesses()
|
||||
if (remainingProcesses.length < chromiumProcesses.length) {
|
||||
console.log(`✅ Cleanup successful: ${chromiumProcesses.length - remainingProcesses.length} processes terminated`)
|
||||
// In development, still check for completion flags before cleanup
|
||||
const isAnalysisComplete = await this.checkAnalysisCompletion()
|
||||
if (isAnalysisComplete) {
|
||||
console.log('✅ Analysis complete, safe to cleanup Chromium processes')
|
||||
await this.cleanupChromeProcessesOnly()
|
||||
} else {
|
||||
console.log(`⚠️ No processes were terminated, ${remainingProcesses.length} still running`)
|
||||
console.log('⏳ Analysis still running, skipping cleanup to prevent interference')
|
||||
}
|
||||
|
||||
// Clean up temp directories and shared memory
|
||||
try {
|
||||
await execAsync('rm -rf /tmp/puppeteer_dev_chrome_profile-* 2>/dev/null || true')
|
||||
await execAsync('rm -rf /dev/shm/.org.chromium.* 2>/dev/null || true')
|
||||
await execAsync('rm -rf /tmp/.org.chromium.* 2>/dev/null || true')
|
||||
console.log('✅ Cleaned up temporary files and shared memory')
|
||||
} catch (error) {
|
||||
console.error('Warning: Could not clean up temporary files:', error)
|
||||
}
|
||||
|
||||
console.log('✅ Post-analysis cleanup completed successfully')
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error in post-analysis cleanup:', error)
|
||||
}
|
||||
|
||||
// Clear any stuck progress sessions
|
||||
try {
|
||||
const { progressTracker } = await import('./progress-tracker')
|
||||
const activeSessions = progressTracker.getActiveSessions()
|
||||
|
||||
if (activeSessions.length > 0) {
|
||||
console.log(`🧹 Force clearing ${activeSessions.length} potentially stuck sessions`)
|
||||
activeSessions.forEach(session => {
|
||||
console.log(`🧹 Force clearing session: ${session}`)
|
||||
progressTracker.deleteSession(session)
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Could not clear progress sessions:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Signal that an analysis cycle is complete and all processes should be cleaned up
|
||||
async signalAnalysisCycleComplete(): Promise<void> {
|
||||
console.log('🎯 Analysis cycle completion signal received')
|
||||
|
||||
// Wait for graceful shutdown of analysis-related processes
|
||||
console.log('⏳ Waiting 5 seconds for graceful process shutdown...')
|
||||
await new Promise(resolve => setTimeout(resolve, 5000))
|
||||
|
||||
// Check if there are any active progress sessions first
|
||||
const activeSessions = await this.checkActiveAnalysisSessions()
|
||||
if (activeSessions > 0) {
|
||||
console.log(`⚠️ Found ${activeSessions} active analysis sessions, skipping aggressive cleanup`)
|
||||
return
|
||||
}
|
||||
|
||||
// Only run cleanup if no active sessions
|
||||
console.log('🧹 No active sessions detected, running post-analysis cleanup...')
|
||||
await this.cleanupPostAnalysisProcesses()
|
||||
console.log('🧹 Post-analysis cleanup triggered...')
|
||||
|
||||
// Small delay to ensure analysis processes are fully closed
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
|
||||
await this.cleanupOrphanedProcesses()
|
||||
}
|
||||
|
||||
private async checkActiveAnalysisSessions(): Promise<number> {
|
||||
// Check if progress tracker has any active sessions
|
||||
private async checkAnalysisCompletion(): Promise<boolean> {
|
||||
try {
|
||||
// This is a simple check - in a real scenario you might want to check actual session state
|
||||
const { stdout } = await execAsync('pgrep -f "automation-.*-.*" | wc -l')
|
||||
return parseInt(stdout.trim()) || 0
|
||||
// Check if analysis completion flag exists
|
||||
const { analysisCompletionFlag } = await import('./analysis-completion-flag')
|
||||
return analysisCompletionFlag.canCleanup()
|
||||
} catch (error) {
|
||||
return 0
|
||||
console.log('⚠️ Could not check analysis completion, assuming it is complete')
|
||||
return true // Assume complete if we can't check
|
||||
}
|
||||
}
|
||||
|
||||
private async cleanupPostAnalysisProcesses(): Promise<void> {
|
||||
console.log('🚨 Post-analysis cleanup - targeting orphaned browser processes')
|
||||
private async cleanupChromeProcessesOnly(): Promise<void> {
|
||||
console.log('🧹 Cleaning up Chromium processes only...')
|
||||
|
||||
try {
|
||||
// Find all chromium processes
|
||||
const chromiumProcesses = await this.findChromiumProcesses()
|
||||
|
||||
if (chromiumProcesses.length === 0) {
|
||||
console.log('✅ No chromium processes found to clean up')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`🔍 Found ${chromiumProcesses.length} chromium processes`)
|
||||
|
||||
// Filter out processes that are too new (less than 2 minutes old)
|
||||
const oldProcesses = await this.filterOldProcesses(chromiumProcesses, 2 * 60) // 2 minutes
|
||||
|
||||
if (oldProcesses.length === 0) {
|
||||
console.log('✅ All chromium processes are recent, not cleaning up')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`🧹 Cleaning up ${oldProcesses.length} old chromium processes`)
|
||||
|
||||
// Try graceful shutdown first
|
||||
for (const pid of oldProcesses) {
|
||||
// First pass - graceful termination
|
||||
const gracefulCommands = [
|
||||
"pkill -TERM -f 'chromium.*--remote-debugging-port' || true",
|
||||
"pkill -TERM -f 'chromium.*--user-data-dir' || true",
|
||||
"pkill -TERM -f '/usr/lib/chromium/chromium' || true",
|
||||
"pkill -TERM -f chrome || true"
|
||||
]
|
||||
|
||||
for (const cmd of gracefulCommands) {
|
||||
try {
|
||||
console.log(`<EFBFBD> Attempting graceful shutdown of process ${pid}`)
|
||||
await execAsync(`kill -TERM ${pid}`)
|
||||
await execAsync(cmd)
|
||||
} catch (error) {
|
||||
console.log(`ℹ️ Process ${pid} may already be terminated`)
|
||||
// Ignore errors - processes might not exist
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for graceful shutdown
|
||||
await new Promise(resolve => setTimeout(resolve, 3000))
|
||||
|
||||
// Check which processes are still running and force kill only those
|
||||
const stillRunning = await this.findStillRunningProcesses(oldProcesses)
|
||||
|
||||
if (stillRunning.length > 0) {
|
||||
console.log(`🗡️ Force killing ${stillRunning.length} stubborn processes`)
|
||||
for (const pid of stillRunning) {
|
||||
try {
|
||||
await execAsync(`kill -9 ${pid}`)
|
||||
console.log(`💀 Force killed process ${pid}`)
|
||||
} catch (error) {
|
||||
console.log(`ℹ️ Process ${pid} already terminated`)
|
||||
}
|
||||
|
||||
// Wait 2 seconds for graceful shutdown
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
|
||||
// Second pass - force kill
|
||||
const forceCommands = [
|
||||
"pkill -9 -f 'chromium.*--remote-debugging-port' || true",
|
||||
"pkill -9 -f 'chromium.*--user-data-dir' || true",
|
||||
"pkill -9 -f '/usr/lib/chromium/chromium' || true",
|
||||
"pkill -9 -f chrome || true",
|
||||
"pkill -9 -f 'type=zygote' || true",
|
||||
"pkill -9 -f 'type=gpu-process' || true",
|
||||
"pkill -9 -f 'type=utility' || true",
|
||||
"pkill -9 -f 'defunct' || true"
|
||||
]
|
||||
|
||||
for (const cmd of forceCommands) {
|
||||
try {
|
||||
await execAsync(cmd)
|
||||
} catch (error) {
|
||||
// Ignore errors - processes might not exist
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ Post-analysis cleanup completed')
|
||||
|
||||
|
||||
console.log('✅ Chromium processes cleanup completed')
|
||||
} catch (error) {
|
||||
console.error('Error in post-analysis cleanup:', error)
|
||||
console.error('❌ Error in Chromium cleanup:', error)
|
||||
}
|
||||
}
|
||||
|
||||
private async findStillRunningProcesses(pids: string[]): Promise<string[]> {
|
||||
const stillRunning: string[] = []
|
||||
// Force cleanup after successful trade execution
|
||||
async forceCleanupAfterTrade(): Promise<void> {
|
||||
console.log('💰 Trade executed - forcing cleanup of Chromium processes')
|
||||
|
||||
for (const pid of pids) {
|
||||
try {
|
||||
await execAsync(`kill -0 ${pid}`) // Check if process exists
|
||||
stillRunning.push(pid)
|
||||
} catch (error) {
|
||||
// Process is already dead
|
||||
}
|
||||
}
|
||||
// Wait longer to ensure analysis is completely done and no new analysis starts
|
||||
await new Promise(resolve => setTimeout(resolve, 10000)) // 10 seconds
|
||||
|
||||
return stillRunning
|
||||
}
|
||||
|
||||
// Method to get detailed process information for debugging
|
||||
async getProcessInfo(): Promise<void> {
|
||||
try {
|
||||
console.log('🔍 Current browser process information:')
|
||||
|
||||
// Get all chromium processes with detailed info
|
||||
const { stdout } = await execAsync('ps aux | grep -E "(chromium|chrome)" | grep -v grep')
|
||||
const processes = stdout.trim().split('\n').filter(line => line.length > 0)
|
||||
|
||||
if (processes.length === 0) {
|
||||
console.log('✅ No browser processes currently running')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`📊 Found ${processes.length} browser processes:`)
|
||||
processes.forEach((process, index) => {
|
||||
const parts = process.split(/\s+/)
|
||||
const pid = parts[1]
|
||||
const cpu = parts[2]
|
||||
const mem = parts[3]
|
||||
const command = parts.slice(10).join(' ')
|
||||
console.log(` ${index + 1}. PID: ${pid}, CPU: ${cpu}%, MEM: ${mem}%, CMD: ${command.substring(0, 100)}...`)
|
||||
})
|
||||
|
||||
// Get memory usage
|
||||
const { stdout: memInfo } = await execAsync('free -h')
|
||||
console.log('💾 Memory usage:')
|
||||
console.log(memInfo)
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error getting process info:', error)
|
||||
// Check if analysis is still running
|
||||
const analysisComplete = await this.checkAnalysisCompletion()
|
||||
if (analysisComplete) {
|
||||
console.log('✅ Analysis confirmed complete, proceeding with cleanup')
|
||||
await this.cleanupChromeProcessesOnly()
|
||||
} else {
|
||||
console.log('⏳ Analysis still active, skipping cleanup to prevent interference')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ export interface AutomationConfig {
|
||||
mode: 'SIMULATION' | 'LIVE'
|
||||
symbol: string
|
||||
timeframe: string
|
||||
selectedTimeframes: string[] // Multi-timeframe support
|
||||
tradingAmount: number
|
||||
maxLeverage: number
|
||||
stopLossPercent: number
|
||||
@@ -63,9 +62,6 @@ export class AutomationService {
|
||||
this.isRunning = true
|
||||
|
||||
console.log(`🤖 Starting automation for ${config.symbol} ${config.timeframe} in ${config.mode} mode`)
|
||||
console.log(`📊 Using timeframes: ${config.selectedTimeframes?.join(", ") || "default fallback"}`)
|
||||
console.log(`📊 Timeframes array:`, config.selectedTimeframes)
|
||||
console.log(`🔧 Full config:`, JSON.stringify(config, null, 2))
|
||||
|
||||
// Ensure user exists in database
|
||||
await prisma.user.upsert({
|
||||
@@ -99,7 +95,6 @@ export class AutomationService {
|
||||
timeframe: config.timeframe,
|
||||
settings: {
|
||||
tradingAmount: config.tradingAmount,
|
||||
selectedTimeframes: config.selectedTimeframes,
|
||||
maxLeverage: config.maxLeverage,
|
||||
stopLossPercent: config.stopLossPercent,
|
||||
takeProfitPercent: config.takeProfitPercent,
|
||||
@@ -279,7 +274,7 @@ export class AutomationService {
|
||||
progressTracker.updateStep(sessionId, 'init', 'active', 'Starting multi-timeframe analysis...')
|
||||
|
||||
// Multi-timeframe analysis: 15m, 1h, 2h, 4h
|
||||
const timeframes = this.config!.selectedTimeframes && this.config!.selectedTimeframes.length > 0 ? this.config!.selectedTimeframes : ["15", "60", "120", "240"]
|
||||
const timeframes = ['15', '1h', '2h', '4h']
|
||||
const symbol = this.config!.symbol
|
||||
|
||||
console.log(`🔍 Analyzing ${symbol} across timeframes: ${timeframes.join(', ')} with AI + DIY layouts`)
|
||||
@@ -598,12 +593,17 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r.
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
// Log the trading signal
|
||||
if (analysis.recommendation === "SELL") {
|
||||
console.log("📉 SELL signal detected - Opening SHORT position")
|
||||
} else if (analysis.recommendation === "BUY") {
|
||||
console.log("📈 BUY signal detected - Opening LONG position")
|
||||
// ✅ ENHANCED: Support both BUY and SELL signals
|
||||
if (analysis.recommendation === 'SELL') {
|
||||
// Check if we have SOL position to sell
|
||||
const hasPosition = await this.checkCurrentPosition()
|
||||
if (!hasPosition) {
|
||||
console.log('📊 SELL signal but no SOL position to sell - skipping')
|
||||
return null
|
||||
}
|
||||
console.log('📉 SELL signal detected with existing SOL position')
|
||||
} else if (analysis.recommendation === 'BUY') {
|
||||
console.log('📈 BUY signal detected')
|
||||
}
|
||||
|
||||
// Calculate position size based on risk percentage
|
||||
@@ -625,6 +625,40 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r.
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ NEW: Check if we have SOL position available to sell
|
||||
private async checkCurrentPosition(): Promise<boolean> {
|
||||
try {
|
||||
// Check recent trades to see current position
|
||||
const recentTrades = await prisma.trade.findMany({
|
||||
where: {
|
||||
userId: this.config!.userId,
|
||||
symbol: this.config!.symbol,
|
||||
status: 'OPEN'
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 5
|
||||
})
|
||||
|
||||
// Count open positions
|
||||
let netPosition = 0
|
||||
for (const trade of recentTrades) {
|
||||
if (trade.side === 'BUY') {
|
||||
netPosition += trade.amount
|
||||
} else if (trade.side === 'SELL') {
|
||||
netPosition -= trade.amount
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`🔍 Current SOL position: ${netPosition.toFixed(4)} SOL`)
|
||||
return netPosition > 0.001 // Have at least 0.001 SOL to sell
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error checking current position:', error)
|
||||
// If we can't check, default to allowing the trade (fail-safe)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private async calculatePositionSize(analysis: any): Promise<number> {
|
||||
const baseAmount = this.config!.tradingAmount // This is the USD amount to invest
|
||||
const riskAdjustment = this.config!.riskPercentage / 100
|
||||
@@ -771,7 +805,7 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r.
|
||||
if (tradeResult.status !== 'FAILED') {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await aggressiveCleanup.runPostAnalysisCleanup()
|
||||
await aggressiveCleanup.forceCleanupAfterTrade()
|
||||
} catch (error) {
|
||||
console.error('Error in post-trade cleanup:', error)
|
||||
}
|
||||
@@ -818,53 +852,52 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r.
|
||||
}
|
||||
|
||||
private async executeLiveTrade(decision: any): Promise<any> {
|
||||
try {
|
||||
console.log(`🚀 Executing DRIFT trade: ${decision.direction} ${decision.positionSize} ${this.config!.symbol} with ${this.config!.maxLeverage}x leverage`)
|
||||
|
||||
const response = await fetch("http://localhost:3000/api/automation/trade", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dexProvider: "DRIFT",
|
||||
action: decision.direction.toLowerCase() === "buy" ? "open_long" : "open_short",
|
||||
symbol: this.config!.symbol.replace("USD", ""), // Convert SOLUSD to SOL
|
||||
amount: this.config!.tradingAmount,
|
||||
side: decision.direction,
|
||||
leverage: this.config!.maxLeverage,
|
||||
stopLoss: decision.stopLoss,
|
||||
takeProfit: decision.takeProfit,
|
||||
mode: "LIVE"
|
||||
})
|
||||
})
|
||||
// Execute real trade via Jupiter DEX
|
||||
const inputToken = decision.direction === 'BUY' ? 'USDC' : 'SOL'
|
||||
const outputToken = decision.direction === 'BUY' ? 'SOL' : 'USDC'
|
||||
|
||||
const tokens = {
|
||||
SOL: 'So11111111111111111111111111111111111111112',
|
||||
USDC: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Drift trade request failed: ${response.statusText}`)
|
||||
}
|
||||
// Calculate proper amount for Jupiter API
|
||||
let swapAmount
|
||||
if (decision.direction === 'BUY') {
|
||||
// BUY: Use trading amount in USDC (convert to 6 decimals)
|
||||
swapAmount = Math.floor(this.config!.tradingAmount * 1e6) // USDC has 6 decimals
|
||||
console.log(`💱 BUY: Converting $${this.config!.tradingAmount} USDC to ${swapAmount} USDC tokens`)
|
||||
} else {
|
||||
// SELL: Use SOL amount (convert to 9 decimals)
|
||||
swapAmount = Math.floor(decision.positionSize * 1e9) // SOL has 9 decimals
|
||||
console.log(`💱 SELL: Converting ${decision.positionSize} SOL to ${swapAmount} SOL tokens`)
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
if (result.success) {
|
||||
return {
|
||||
transactionId: result.result?.transactionId || result.txId,
|
||||
executionPrice: result.result?.executionPrice || decision.currentPrice,
|
||||
amount: result.result?.amount || decision.positionSize,
|
||||
direction: decision.direction,
|
||||
status: "COMPLETED",
|
||||
timestamp: new Date(),
|
||||
fees: result.result?.fees || 0,
|
||||
slippage: result.result?.slippage || 0,
|
||||
leverage: this.config!.maxLeverage,
|
||||
dexProvider: "DRIFT",
|
||||
tradingAmount: this.config!.tradingAmount
|
||||
}
|
||||
} else {
|
||||
throw new Error(result.error || "Drift trade execution failed")
|
||||
console.log(`🔄 Executing Jupiter swap with corrected amount: ${swapAmount}`)
|
||||
|
||||
const swapResult = await jupiterDEXService.executeSwap(
|
||||
tokens[inputToken as keyof typeof tokens],
|
||||
tokens[outputToken as keyof typeof tokens],
|
||||
swapAmount,
|
||||
50 // 0.5% slippage
|
||||
)
|
||||
|
||||
// Convert Jupiter result to standard trade result format
|
||||
if (swapResult.success) {
|
||||
return {
|
||||
transactionId: swapResult.txId,
|
||||
executionPrice: swapResult.executionPrice,
|
||||
amount: swapResult.outputAmount, // Amount of tokens received
|
||||
direction: decision.direction,
|
||||
status: 'COMPLETED',
|
||||
timestamp: new Date(),
|
||||
fees: swapResult.fees || 0,
|
||||
slippage: swapResult.slippage || 0,
|
||||
inputAmount: swapResult.inputAmount, // Amount of tokens spent
|
||||
tradingAmount: this.config!.tradingAmount // Original USD amount
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Live trade execution error:", error)
|
||||
throw error
|
||||
} else {
|
||||
throw new Error(swapResult.error || 'Jupiter swap failed')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1072,7 +1105,6 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r.
|
||||
mode: session.mode,
|
||||
symbol: session.symbol,
|
||||
timeframe: session.timeframe,
|
||||
selectedTimeframes: settings.selectedTimeframes || ["60", "240"], // Default fallback
|
||||
tradingAmount: settings.tradingAmount || 100,
|
||||
maxLeverage: settings.maxLeverage || 3,
|
||||
stopLossPercent: settings.stopLossPercent || 2,
|
||||
|
||||
Reference in New Issue
Block a user