import { PrismaClient } from '@prisma/client' import { aiAnalysisService, AnalysisResult } from './ai-analysis' import { jupiterDEXService } from './jupiter-dex-service' import { enhancedScreenshotService } from './enhanced-screenshot-simple' const prisma = new PrismaClient() export interface AutomationConfig { userId: string mode: 'SIMULATION' | 'LIVE' symbol: string timeframe: string tradingAmount: number maxLeverage: number stopLossPercent: number takeProfitPercent: number maxDailyTrades: number riskPercentage: number } export interface AutomationStatus { isActive: boolean mode: 'SIMULATION' | 'LIVE' symbol: string timeframe: string totalTrades: number successfulTrades: number winRate: number totalPnL: number lastAnalysis?: Date lastTrade?: Date nextScheduled?: Date errorCount: number lastError?: string } export class AutomationService { private isRunning = false private config: AutomationConfig | null = null private intervalId: NodeJS.Timeout | null = null private stats = { totalTrades: 0, successfulTrades: 0, winRate: 0, totalPnL: 0, errorCount: 0, lastError: null as string | null } async startAutomation(config: AutomationConfig): Promise { try { if (this.isRunning) { throw new Error('Automation is already running') } this.config = config this.isRunning = true console.log(`πŸ€– Starting automation for ${config.symbol} ${config.timeframe} in ${config.mode} mode`) // Create automation session in database await prisma.automationSession.create({ data: { userId: config.userId, status: 'ACTIVE', mode: config.mode, symbol: config.symbol, timeframe: config.timeframe, settings: { tradingAmount: config.tradingAmount, maxLeverage: config.maxLeverage, stopLossPercent: config.stopLossPercent, takeProfitPercent: config.takeProfitPercent, maxDailyTrades: config.maxDailyTrades, riskPercentage: config.riskPercentage }, startBalance: config.tradingAmount, currentBalance: config.tradingAmount, createdAt: new Date(), updatedAt: new Date() } }) // Start automation cycle this.startAutomationCycle() return true } catch (error) { console.error('Failed to start automation:', error) this.stats.errorCount++ this.stats.lastError = error instanceof Error ? error.message : 'Unknown error' return false } } private startAutomationCycle(): void { if (!this.config) return // Get interval in milliseconds based on timeframe const intervalMs = this.getIntervalFromTimeframe(this.config.timeframe) console.log(`πŸ”„ Starting automation cycle every ${intervalMs/1000} seconds`) this.intervalId = setInterval(async () => { if (this.isRunning && this.config) { await this.runAutomationCycle() } }, intervalMs) // Run first cycle immediately this.runAutomationCycle() } private getIntervalFromTimeframe(timeframe: string): number { const intervals: { [key: string]: number } = { '1m': 60 * 1000, '3m': 3 * 60 * 1000, '5m': 5 * 60 * 1000, '15m': 15 * 60 * 1000, '30m': 30 * 60 * 1000, '1h': 60 * 60 * 1000, '2h': 2 * 60 * 60 * 1000, '4h': 4 * 60 * 60 * 1000, '1d': 24 * 60 * 60 * 1000 } return intervals[timeframe] || intervals['1h'] // Default to 1 hour } private async runAutomationCycle(): Promise { if (!this.config) return try { console.log(`πŸ” Running automation cycle for ${this.config.symbol} ${this.config.timeframe}`) // Step 1: Check daily trade limit const todayTrades = await this.getTodayTradeCount(this.config.userId) if (todayTrades >= this.config.maxDailyTrades) { console.log(`πŸ“Š Daily trade limit reached (${todayTrades}/${this.config.maxDailyTrades})`) return } // Step 2: Take screenshot and analyze const analysisResult = await this.performAnalysis() if (!analysisResult) { console.log('❌ Analysis failed, skipping cycle') return } // Step 3: Store analysis for learning await this.storeAnalysisForLearning(analysisResult) // Step 4: Make trading decision const tradeDecision = await this.makeTradeDecision(analysisResult) if (!tradeDecision) { console.log('πŸ“Š No trading opportunity found') return } // Step 5: Execute trade await this.executeTrade(tradeDecision) } catch (error) { console.error('Error in automation cycle:', error) this.stats.errorCount++ this.stats.lastError = error instanceof Error ? error.message : 'Unknown error' } } private async performAnalysis(): Promise<{ screenshots: string[] analysis: AnalysisResult | null } | null> { try { console.log('πŸ“Έ Taking screenshot and analyzing...') const screenshotConfig = { symbol: this.config!.symbol, timeframe: this.config!.timeframe, layouts: ['ai', 'diy'] } const result = await aiAnalysisService.captureAndAnalyzeWithConfig(screenshotConfig) if (!result.analysis || result.screenshots.length === 0) { console.log('❌ No analysis or screenshots captured') return null } console.log(`βœ… Analysis completed: ${result.analysis.recommendation} with ${result.analysis.confidence}% confidence`) return result } catch (error) { console.error('Error performing analysis:', error) return null } } private async storeAnalysisForLearning(result: { screenshots: string[] analysis: AnalysisResult | null }): Promise { try { if (!result.analysis) return await prisma.aILearningData.create({ data: { userId: this.config!.userId, symbol: this.config!.symbol, timeframe: this.config!.timeframe, screenshot: result.screenshots[0] || '', analysisData: JSON.stringify(result.analysis), marketConditions: JSON.stringify({ marketSentiment: result.analysis.marketSentiment, keyLevels: result.analysis.keyLevels, timestamp: new Date().toISOString() }), confidenceScore: result.analysis.confidence, createdAt: new Date() } }) } catch (error) { console.error('Error storing analysis for learning:', error) } } private async makeTradeDecision(result: { screenshots: string[] analysis: AnalysisResult | null }): Promise { try { const analysis = result.analysis if (!analysis) return null // Only trade if confidence is high enough if (analysis.confidence < 70) { console.log(`πŸ“Š Confidence too low: ${analysis.confidence}%`) return null } // Only trade if direction is clear if (analysis.recommendation === 'HOLD') { console.log('πŸ“Š No clear direction signal') return null } // Calculate position size based on risk percentage const positionSize = this.calculatePositionSize(analysis) return { direction: analysis.recommendation, confidence: analysis.confidence, positionSize, stopLoss: this.calculateStopLoss(analysis), takeProfit: this.calculateTakeProfit(analysis), marketSentiment: analysis.marketSentiment } } catch (error) { console.error('Error making trade decision:', error) return null } } private calculatePositionSize(analysis: any): number { const baseAmount = this.config!.tradingAmount const riskAdjustment = this.config!.riskPercentage / 100 const confidenceAdjustment = analysis.confidence / 100 return baseAmount * riskAdjustment * confidenceAdjustment } private calculateStopLoss(analysis: any): number { const currentPrice = analysis.currentPrice || 0 const stopLossPercent = this.config!.stopLossPercent / 100 if (analysis.direction === 'LONG') { return currentPrice * (1 - stopLossPercent) } else { return currentPrice * (1 + stopLossPercent) } } private calculateTakeProfit(analysis: any): number { const currentPrice = analysis.currentPrice || 0 const takeProfitPercent = this.config!.takeProfitPercent / 100 if (analysis.direction === 'LONG') { return currentPrice * (1 + takeProfitPercent) } else { return currentPrice * (1 - takeProfitPercent) } } private async executeTrade(decision: any): Promise { try { console.log(`🎯 Executing ${this.config!.mode} trade: ${decision.direction} ${decision.positionSize} ${this.config!.symbol}`) let tradeResult: any if (this.config!.mode === 'SIMULATION') { // Execute simulation trade tradeResult = await this.executeSimulationTrade(decision) } else { // Execute live trade via Jupiter tradeResult = await this.executeLiveTrade(decision) } // Store trade in database await this.storeTrade(decision, tradeResult) // Update stats this.updateStats(tradeResult) console.log(`βœ… Trade executed successfully: ${tradeResult.transactionId || 'SIMULATION'}`) } catch (error) { console.error('Error executing trade:', error) this.stats.errorCount++ this.stats.lastError = error instanceof Error ? error.message : 'Trade execution failed' } } private async executeSimulationTrade(decision: any): Promise { // Simulate trade execution with realistic parameters const currentPrice = decision.currentPrice || 100 // Mock price const slippage = Math.random() * 0.005 // 0-0.5% slippage const executionPrice = currentPrice * (1 + (Math.random() > 0.5 ? slippage : -slippage)) return { transactionId: `SIM_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, executionPrice, amount: decision.positionSize, direction: decision.direction, status: 'COMPLETED', timestamp: new Date(), fees: decision.positionSize * 0.001, // 0.1% fee slippage: slippage * 100 } } private async executeLiveTrade(decision: any): Promise { // 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', } return await jupiterDEXService.executeSwap( tokens[inputToken as keyof typeof tokens], tokens[outputToken as keyof typeof tokens], decision.positionSize, 50 // 0.5% slippage ) } private async storeTrade(decision: any, result: any): Promise { try { await prisma.trade.create({ data: { userId: this.config!.userId, symbol: this.config!.symbol, side: decision.direction, amount: decision.positionSize, price: result.executionPrice, status: result.status, driftTxId: result.transactionId || result.txId, fees: result.fees || 0, stopLoss: decision.stopLoss, takeProfit: decision.takeProfit, isAutomated: true, tradingMode: this.config!.mode, confidence: decision.confidence, marketSentiment: decision.marketSentiment, createdAt: new Date() } }) } catch (error) { console.error('Error storing trade:', error) } } private updateStats(result: any): void { this.stats.totalTrades++ if (result.status === 'COMPLETED') { this.stats.successfulTrades++ this.stats.winRate = (this.stats.successfulTrades / this.stats.totalTrades) * 100 // Update PnL (simplified calculation) const pnl = result.amount * 0.01 * (Math.random() > 0.5 ? 1 : -1) // Random PnL for demo this.stats.totalPnL += pnl } } private async getTodayTradeCount(userId: string): Promise { const today = new Date() today.setHours(0, 0, 0, 0) const count = await prisma.trade.count({ where: { userId, isAutomated: true, createdAt: { gte: today } } }) return count } async stopAutomation(): Promise { try { this.isRunning = false this.config = null console.log('πŸ›‘ Automation stopped') return true } catch (error) { console.error('Failed to stop automation:', error) return false } } async pauseAutomation(): Promise { try { if (!this.isRunning) { return false } this.isRunning = false console.log('⏸️ Automation paused') return true } catch (error) { console.error('Failed to pause automation:', error) return false } } async resumeAutomation(): Promise { try { if (!this.config) { return false } this.isRunning = true console.log('▢️ Automation resumed') return true } catch (error) { console.error('Failed to resume automation:', error) return false } } async getStatus(): Promise { try { if (!this.config) { return null } return { isActive: this.isRunning, mode: this.config.mode, symbol: this.config.symbol, timeframe: this.config.timeframe, totalTrades: this.stats.totalTrades, successfulTrades: this.stats.successfulTrades, winRate: this.stats.winRate, totalPnL: this.stats.totalPnL, errorCount: this.stats.errorCount, lastError: this.stats.lastError || undefined, lastAnalysis: new Date(), lastTrade: new Date() } } catch (error) { console.error('Failed to get automation status:', error) return null } } async getLearningInsights(userId: string): Promise<{ totalAnalyses: number avgAccuracy: number bestTimeframe: string worstTimeframe: string commonFailures: string[] recommendations: string[] }> { try { // For now, return mock data return { totalAnalyses: 150, avgAccuracy: 0.72, bestTimeframe: '1h', worstTimeframe: '15m', commonFailures: [ 'Low confidence predictions', 'Missed support/resistance levels', 'Timeframe misalignment' ], recommendations: [ 'Focus on 1h timeframe for better accuracy', 'Wait for higher confidence signals (>75%)', 'Use multiple timeframe confirmation' ] } } catch (error) { console.error('Failed to get learning insights:', error) return { totalAnalyses: 0, avgAccuracy: 0, bestTimeframe: 'Unknown', worstTimeframe: 'Unknown', commonFailures: [], recommendations: [] } } } } export const automationService = new AutomationService()