ADVANCED SYSTEM KNOWLEDGE: - Superior parallel screenshot system (60% performance gain) - AI learning system architecture and decision flow - Orphaned order cleanup integration patterns - Critical technical fixes and troubleshooting guide - Database schema best practices - Memory leak prevention strategies - AI learning system patterns and functions - Error handling best practices for trading systems - Integration patterns for position monitoring - Performance optimization rules - UI/UX consistency requirements - Critical anti-patterns to avoid - Added links to new knowledge base documents - Comprehensive documentation structure - Development guides and best practices - Performance optimizations summary - 60% screenshot performance improvement techniques - AI learning system that adapts trading decisions - Container stability and crash prevention - Frontend-backend consistency requirements - Integration strategies for existing infrastructure This documentation preserves critical insights from complex debugging sessions and provides patterns for future development.
327 lines
11 KiB
TypeScript
327 lines
11 KiB
TypeScript
class SafeParallelAutomation {
|
|
private isRunning = false
|
|
private config: any = null
|
|
private intervalId: NodeJS.Timeout | null = null
|
|
private lastAnalysisTime = 0
|
|
|
|
// SAFE PARAMETERS - Respect timeframe strategy
|
|
private readonly MIN_ANALYSIS_INTERVAL = 10 * 60 * 1000 // 10 minutes minimum
|
|
private readonly MAX_TRADES_PER_HOUR = 2
|
|
private readonly ANALYSIS_COOLDOWN = 5 * 60 * 1000 // 5 minutes
|
|
|
|
private stats = {
|
|
totalTrades: 0,
|
|
successfulTrades: 0,
|
|
winRate: 0,
|
|
totalPnL: 0,
|
|
errorCount: 0,
|
|
lastAnalysis: null as string | null,
|
|
nextScheduled: null as string | null,
|
|
nextAnalysisIn: 0,
|
|
analysisInterval: 0,
|
|
currentCycle: 0,
|
|
lastError: null as string | null,
|
|
strategyType: 'General'
|
|
}
|
|
|
|
async startSafeAutomation(config: any): Promise<{ success: boolean, message?: string }> {
|
|
try {
|
|
if (this.isRunning) {
|
|
return { success: false, message: 'Safe automation already running' }
|
|
}
|
|
|
|
// SAFETY CHECK: Rate limiting
|
|
const now = Date.now()
|
|
if (now - this.lastAnalysisTime < this.ANALYSIS_COOLDOWN) {
|
|
const remaining = Math.ceil((this.ANALYSIS_COOLDOWN - (now - this.lastAnalysisTime)) / 1000)
|
|
return {
|
|
success: false,
|
|
message: 'Safety cooldown: Wait ' + remaining + ' seconds'
|
|
}
|
|
}
|
|
|
|
// SAFETY CHECK: Validate timeframes
|
|
if (!config.timeframes || config.timeframes.length === 0) {
|
|
return { success: false, message: 'At least one timeframe required' }
|
|
}
|
|
|
|
this.config = config
|
|
this.isRunning = true
|
|
this.lastAnalysisTime = now
|
|
this.stats.currentCycle = 0
|
|
this.stats.strategyType = config.strategyType || 'General'
|
|
|
|
console.log('SAFE PARALLEL: Starting for ' + config.symbol)
|
|
console.log('STRATEGY: ' + this.stats.strategyType)
|
|
console.log('TIMEFRAMES: [' + config.timeframes.join(', ') + ']')
|
|
console.log('SAFETY: Max ' + this.MAX_TRADES_PER_HOUR + ' trades/hour')
|
|
|
|
// Start with strategy-appropriate interval
|
|
this.startSafeAnalysisCycle()
|
|
|
|
return { success: true, message: 'Safe parallel automation started' }
|
|
} catch (error) {
|
|
console.error('Failed to start safe automation:', error)
|
|
return { success: false, message: 'Failed to start automation' }
|
|
}
|
|
}
|
|
|
|
private startSafeAnalysisCycle(): void {
|
|
// Get strategy-appropriate interval but enforce minimum safety
|
|
const strategyInterval = this.getStrategyInterval(this.config.timeframes, this.config.strategyType)
|
|
const safeInterval = Math.max(this.MIN_ANALYSIS_INTERVAL, strategyInterval)
|
|
|
|
console.log('STRATEGY INTERVAL: ' + (strategyInterval / 1000 / 60) + ' minutes')
|
|
console.log('SAFE INTERVAL: ' + (safeInterval / 1000 / 60) + ' minutes (enforced minimum)')
|
|
|
|
this.stats.analysisInterval = safeInterval
|
|
this.stats.nextScheduled = new Date(Date.now() + safeInterval).toISOString()
|
|
this.stats.nextAnalysisIn = safeInterval
|
|
|
|
// Set safe interval
|
|
this.intervalId = setInterval(async () => {
|
|
if (this.isRunning && this.config) {
|
|
await this.runSafeAnalysisCycle()
|
|
}
|
|
}, safeInterval)
|
|
|
|
// First cycle after delay
|
|
setTimeout(() => {
|
|
if (this.isRunning && this.config) {
|
|
this.runSafeAnalysisCycle()
|
|
}
|
|
}, 30000)
|
|
}
|
|
|
|
private async runSafeAnalysisCycle(): Promise<void> {
|
|
try {
|
|
console.log('\nSAFE CYCLE ' + (this.stats.currentCycle + 1) + ': ' + this.config.symbol)
|
|
console.log('STRATEGY: ' + this.stats.strategyType)
|
|
|
|
// SAFETY CHECK
|
|
const canTrade = await this.canExecuteTrade()
|
|
if (!canTrade.allowed) {
|
|
console.log('SAFETY BLOCK: ' + canTrade.reason)
|
|
this.updateNextAnalysisTime()
|
|
return
|
|
}
|
|
|
|
// PARALLEL ANALYSIS using enhanced screenshot API
|
|
console.log('PARALLEL: Analyzing ' + this.config.timeframes.length + ' timeframes...')
|
|
|
|
const analysisResult = await this.performParallelAnalysis()
|
|
|
|
if (!analysisResult || !analysisResult.analysis) {
|
|
console.log('Analysis failed, skipping')
|
|
return
|
|
}
|
|
|
|
this.stats.lastAnalysis = new Date().toISOString()
|
|
console.log('ANALYSIS: ' + analysisResult.analysis.recommendation)
|
|
|
|
// CONTROLLED TRADING with strategy-specific logic
|
|
if (this.shouldExecuteTrade(analysisResult.analysis)) {
|
|
console.log('EXECUTING: Trade criteria met for ' + this.stats.strategyType)
|
|
const tradeResult = await this.executeSafeTrade(analysisResult.analysis)
|
|
if (tradeResult?.success) {
|
|
this.stats.totalTrades++
|
|
this.stats.successfulTrades++
|
|
this.stats.winRate = (this.stats.successfulTrades / this.stats.totalTrades) * 100
|
|
console.log('TRADE: Executed successfully')
|
|
}
|
|
} else {
|
|
console.log('SIGNAL: Criteria not met for ' + this.stats.strategyType)
|
|
}
|
|
|
|
this.stats.currentCycle++
|
|
|
|
} catch (error) {
|
|
console.error('Error in safe cycle:', error)
|
|
this.stats.errorCount++
|
|
this.stats.lastError = error instanceof Error ? error.message : 'Unknown error'
|
|
} finally {
|
|
await this.guaranteedCleanup()
|
|
this.updateNextAnalysisTime()
|
|
}
|
|
}
|
|
|
|
private async performParallelAnalysis(): Promise<any> {
|
|
try {
|
|
// Use the enhanced screenshot API endpoint with all selected timeframes
|
|
const response = await fetch('http://localhost:3000/api/enhanced-screenshot', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
symbol: this.config.symbol,
|
|
timeframes: this.config.timeframes,
|
|
layouts: ['ai', 'diy'],
|
|
analyze: true
|
|
})
|
|
})
|
|
|
|
throw new Error('API response: ' + response.status)
|
|
}
|
|
|
|
const result = await response.json()
|
|
|
|
if (result.analysis) {
|
|
console.log('PARALLEL: Confidence ' + result.analysis.confidence + '%')
|
|
return result
|
|
}
|
|
|
|
return null
|
|
} catch (error) {
|
|
console.error('Error in parallel analysis:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
private shouldExecuteTrade(analysis: any): boolean {
|
|
const recommendation = analysis.recommendation.toLowerCase()
|
|
const confidence = analysis.confidence || 0
|
|
|
|
// Strategy-specific confidence requirements
|
|
let minConfidence = 75 // Default
|
|
|
|
if (this.stats.strategyType === 'Scalping') {
|
|
minConfidence = 80 // Higher confidence for scalping
|
|
} else if (this.stats.strategyType === 'Day Trading') {
|
|
minConfidence = 75 // Standard confidence
|
|
} else if (this.stats.strategyType === 'Swing Trading') {
|
|
minConfidence = 70 // Slightly lower for swing (longer timeframes)
|
|
}
|
|
|
|
const isHighConfidence = confidence >= minConfidence
|
|
const isClearDirection = recommendation.includes('buy') || recommendation.includes('sell')
|
|
|
|
console.log('SIGNAL: ' + recommendation + ' (' + confidence + '%) - Required: ' + minConfidence + '%')
|
|
|
|
return isHighConfidence && isClearDirection
|
|
}
|
|
|
|
private getStrategyInterval(timeframes: string[], strategyType: string): number {
|
|
// Strategy-based intervals (but minimum will be enforced)
|
|
|
|
if (strategyType === 'Scalping') {
|
|
console.log('SCALPING: Using 2-minute analysis cycle')
|
|
return 2 * 60 * 1000 // 2 minutes for scalping (will be enforced to 10 min minimum)
|
|
}
|
|
|
|
if (strategyType === 'Day Trading') {
|
|
console.log('DAY TRADING: Using 5-minute analysis cycle')
|
|
return 5 * 60 * 1000 // 5 minutes for day trading (will be enforced to 10 min minimum)
|
|
}
|
|
|
|
if (strategyType === 'Swing Trading') {
|
|
console.log('SWING TRADING: Using 15-minute analysis cycle')
|
|
return 15 * 60 * 1000 // 15 minutes for swing trading
|
|
}
|
|
|
|
// Fallback based on shortest timeframe
|
|
const shortest = Math.min(...timeframes.map(tf => {
|
|
if (tf === 'D') return 1440
|
|
return parseInt(tf) || 60
|
|
}))
|
|
|
|
if (shortest <= 5) return 2 * 60 * 1000 // 2 min for very short
|
|
if (shortest <= 15) return 5 * 60 * 1000 // 5 min for short
|
|
if (shortest <= 60) return 10 * 60 * 1000 // 10 min for medium
|
|
return 15 * 60 * 1000 // 15 min for long
|
|
}
|
|
|
|
private async executeSafeTrade(analysis: any): Promise<any> {
|
|
try {
|
|
console.log('SAFE TRADE: Executing...')
|
|
|
|
// Use drift trading API endpoint
|
|
const response = await fetch('http://localhost:3000/api/trading', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
mode: this.config.mode,
|
|
symbol: this.config.symbol,
|
|
amount: this.config.tradingAmount,
|
|
leverage: this.config.leverage,
|
|
stopLoss: this.config.stopLoss,
|
|
takeProfit: this.config.takeProfit,
|
|
analysis: analysis,
|
|
strategy: this.stats.strategyType
|
|
})
|
|
})
|
|
|
|
const result = await response.json()
|
|
return result
|
|
} catch (error) {
|
|
console.error('Error executing trade:', error)
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
private async canExecuteTrade(): Promise<{ allowed: boolean, reason?: string }> {
|
|
const now = Date.now()
|
|
|
|
if (now - this.lastAnalysisTime < this.ANALYSIS_COOLDOWN) {
|
|
const remaining = Math.ceil((this.ANALYSIS_COOLDOWN - (now - this.lastAnalysisTime)) / 1000 / 60)
|
|
return { allowed: false, reason: 'Cooldown: ' + remaining + 'min' }
|
|
}
|
|
|
|
if (this.stats.totalTrades >= this.MAX_TRADES_PER_HOUR) {
|
|
return { allowed: false, reason: 'Hour limit: ' + this.stats.totalTrades + '/' + this.MAX_TRADES_PER_HOUR }
|
|
}
|
|
|
|
return { allowed: true }
|
|
}
|
|
|
|
private async guaranteedCleanup(): Promise<void> {
|
|
try {
|
|
const { execSync } = require('child_process')
|
|
execSync('pkill -f "chrome|chromium" 2>/dev/null || true')
|
|
console.log('CLEANUP: Completed')
|
|
} catch (error) {
|
|
console.error('CLEANUP: Failed', error)
|
|
}
|
|
}
|
|
|
|
private updateNextAnalysisTime(): void {
|
|
const nextTime = Date.now() + this.stats.analysisInterval
|
|
this.stats.nextScheduled = new Date(nextTime).toISOString()
|
|
this.stats.nextAnalysisIn = this.stats.analysisInterval
|
|
}
|
|
|
|
async stopSafeAutomation(): Promise<{ success: boolean, message?: string }> {
|
|
try {
|
|
this.isRunning = false
|
|
|
|
if (this.intervalId) {
|
|
clearInterval(this.intervalId)
|
|
this.intervalId = null
|
|
}
|
|
|
|
await this.guaranteedCleanup()
|
|
|
|
this.config = null
|
|
this.stats.nextAnalysisIn = 0
|
|
this.stats.nextScheduled = null
|
|
|
|
console.log('SAFE: Automation stopped')
|
|
return { success: true, message: 'Safe automation stopped' }
|
|
} catch (error) {
|
|
return { success: false, message: 'Stop failed' }
|
|
}
|
|
}
|
|
|
|
getStatus() {
|
|
return {
|
|
isActive: this.isRunning,
|
|
mode: this.config?.mode || 'SIMULATION',
|
|
symbol: this.config?.symbol || 'SOLUSD',
|
|
timeframes: this.config?.timeframes || ['1h'],
|
|
automationType: 'SAFE_PARALLEL',
|
|
strategy: this.stats.strategyType,
|
|
...this.stats
|
|
}
|
|
}
|
|
}
|
|
|
|
export const safeParallelAutomation = new SafeParallelAutomation()
|