diff --git a/app/api/batch-analysis/route.js b/app/api/batch-analysis/route.js new file mode 100644 index 0000000..ccbc5ef --- /dev/null +++ b/app/api/batch-analysis/route.js @@ -0,0 +1,185 @@ +import { NextResponse } from 'next/server' +import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot' +import { aiAnalysisService } from '../../../lib/ai-analysis' +import { progressTracker } from '../../../lib/progress-tracker' + +export async function POST(request) { + try { + const body = await request.json() + const { + symbol, + timeframes = [], + layouts = ['ai'], + sessionId: providedSessionId + } = body + + console.log('๐ฏ Batch analysis request:', { symbol, timeframes, layouts }) + + // Use provided sessionId or generate one + const sessionId = providedSessionId || `batch_analysis_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` + console.log('๐ Using batch session ID:', sessionId) + + // Create progress tracking with batch-specific steps + const totalCaptures = timeframes.length * layouts.length + const initialSteps = [ + { + id: 'init', + title: 'Initializing Batch Analysis', + description: `Preparing to capture ${totalCaptures} screenshots across ${timeframes.length} timeframes`, + status: 'pending' + }, + { + id: 'auth', + title: 'TradingView Authentication', + description: 'Logging into TradingView accounts', + status: 'pending' + }, + { + id: 'batch_capture', + title: 'Batch Screenshot Capture', + description: `Capturing screenshots for all ${timeframes.length} timeframes`, + status: 'pending' + }, + { + id: 'comparative_analysis', + title: 'Comparative AI Analysis', + description: `Analyzing all ${totalCaptures} screenshots together for timeframe comparison`, + status: 'pending' + } + ] + + // Create the progress session + progressTracker.createSession(sessionId, initialSteps) + + let allScreenshots = [] + const timeframeResults = [] + + try { + // Mark init as completed + progressTracker.updateStep(sessionId, 'init', 'completed', 'Batch analysis initialized') + progressTracker.updateStep(sessionId, 'auth', 'active', 'Starting authentication...') + + // Capture screenshots for each timeframe WITHOUT analysis + for (let i = 0; i < timeframes.length; i++) { + const timeframe = timeframes[i] + console.log(`๐ธ Capturing ${timeframe} screenshots (${i + 1}/${timeframes.length})...`) + + // Update progress + progressTracker.updateStep(sessionId, 'batch_capture', 'active', + `Capturing ${timeframe} screenshots (${i + 1}/${timeframes.length})`) + + const config = { + symbol: symbol || 'BTCUSD', + timeframe: timeframe, + layouts: layouts, + sessionId: `${sessionId}_tf_${timeframe}`, // Sub-session for this timeframe + credentials: { + email: process.env.TRADINGVIEW_EMAIL, + password: process.env.TRADINGVIEW_PASSWORD + } + } + + try { + // Capture screenshots only (no analysis) + const screenshots = await enhancedScreenshotService.captureWithLogin(config) + + allScreenshots.push(...screenshots) + timeframeResults.push({ + timeframe, + screenshots: screenshots.length, + files: screenshots + }) + + console.log(`โ ${timeframe}: ${screenshots.length} screenshots captured`) + } catch (error) { + console.error(`โ Failed to capture ${timeframe}:`, error) + timeframeResults.push({ + timeframe, + error: error.message, + screenshots: 0, + files: [] + }) + } + } + + // Mark capture as completed + progressTracker.updateStep(sessionId, 'batch_capture', 'completed', + `All timeframes captured: ${allScreenshots.length} total screenshots`) + + // Now perform comparative analysis on ALL screenshots + console.log(`๐ค Starting comparative analysis of ${allScreenshots.length} screenshots...`) + progressTracker.updateStep(sessionId, 'comparative_analysis', 'active', + `Analyzing ${allScreenshots.length} screenshots for timeframe comparison`) + + let analysis = null + if (allScreenshots.length > 0) { + try { + // Use the analyzeMultipleScreenshots method for comparative analysis + analysis = await aiAnalysisService.analyzeMultipleScreenshots(allScreenshots) + + if (analysis) { + progressTracker.updateStep(sessionId, 'comparative_analysis', 'completed', + 'Comparative analysis completed successfully!') + } else { + progressTracker.updateStep(sessionId, 'comparative_analysis', 'error', + 'Analysis returned no results') + } + } catch (analysisError) { + console.error('โ Comparative analysis failed:', analysisError) + progressTracker.updateStep(sessionId, 'comparative_analysis', 'error', + `Analysis failed: ${analysisError.message}`) + } + } else { + progressTracker.updateStep(sessionId, 'comparative_analysis', 'error', + 'No screenshots available for analysis') + } + + // Cleanup session after delay + setTimeout(() => progressTracker.deleteSession(sessionId), 5000) + + const result = { + success: true, + sessionId, + type: 'batch_comparative', + symbol: symbol || 'BTCUSD', + timeframes, + layouts, + totalScreenshots: allScreenshots.length, + screenshots: allScreenshots, + timeframeBreakdown: timeframeResults, + analysis, + summary: `Captured ${allScreenshots.length} screenshots across ${timeframes.length} timeframes for comparative analysis` + } + + console.log('โ Batch comparative analysis completed:', { + timeframes: timeframes.length, + screenshots: allScreenshots.length, + hasAnalysis: !!analysis + }) + + return NextResponse.json(result) + + } catch (error) { + console.error('โ Batch analysis error:', error) + progressTracker.updateStep(sessionId, 'comparative_analysis', 'error', + `Batch analysis failed: ${error.message}`) + + return NextResponse.json({ + success: false, + error: error.message, + sessionId, + partialResults: { + timeframeResults, + screenshotsCaptured: allScreenshots.length + } + }, { status: 500 }) + } + + } catch (error) { + console.error('โ Batch analysis request error:', error) + return NextResponse.json({ + success: false, + error: error.message + }, { status: 500 }) + } +} diff --git a/components/AIAnalysisPanel.tsx b/components/AIAnalysisPanel.tsx index 5fca6c3..f7db9b7 100644 --- a/components/AIAnalysisPanel.tsx +++ b/components/AIAnalysisPanel.tsx @@ -306,68 +306,60 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP onAnalysisComplete(data.analysis, analysisSymbol) } } else { - // Multiple timeframe analysis - const results = [] + // Multiple timeframe analysis - use batch comparative analysis + console.log(`๐ฏ Starting batch comparative analysis for ${analysisTimeframes.length} timeframes`) - for (let i = 0; i < analysisTimeframes.length; i++) { - const tf = analysisTimeframes[i] - const timeframeLabel = timeframes.find(t => t.value === tf)?.label || tf - - console.log(`๐งช Analyzing timeframe: ${timeframeLabel}`) - - const response = await fetch('/api/enhanced-screenshot', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - symbol: analysisSymbol, - timeframe: tf, - layouts: selectedLayouts, - analyze: true - }) - }) - - const result = await response.json() - results.push({ - timeframe: tf, - timeframeLabel, - success: response.ok, - result + // Pre-generate sessionId for batch analysis + const sessionId = `batch_analysis_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` + startProgressTracking(sessionId) + + const response = await fetch('/api/batch-analysis', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + symbol: analysisSymbol, + timeframes: analysisTimeframes, + layouts: selectedLayouts, + sessionId: sessionId }) + }) - // Start progress tracking for the first timeframe session - if (i === 0 && result.sessionId) { - startProgressTracking(result.sessionId) - } - - // Update timeframe progress manually for multi-timeframe - setProgress(prev => prev ? { - ...prev, - timeframeProgress: { - current: i + 1, - total: analysisTimeframes.length, - currentTimeframe: timeframeLabel - } - } : null) - - // Small delay between requests - await new Promise(resolve => setTimeout(resolve, 1000)) - } + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || 'Batch analysis failed') + } + + console.log(`โ Batch analysis completed: ${data.totalScreenshots} screenshots, ${data.timeframes.length} timeframes`) + + // Transform batch result to match expected format for UI compatibility const multiResult = { - type: 'multi_timeframe', + type: 'batch_comparative', symbol: analysisSymbol, - summary: `Analyzed ${results.length} timeframes for ${analysisSymbol}`, - results + summary: data.summary || `Batch comparative analysis of ${data.timeframes?.length || analysisTimeframes.length} timeframes`, + analysis: data.analysis, + screenshots: data.screenshots, + timeframeBreakdown: data.timeframeBreakdown, + totalScreenshots: data.totalScreenshots, + timeframes: analysisTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label || tf), + // Create results array for UI compatibility + results: analysisTimeframes.map(tf => ({ + timeframe: tf, + timeframeLabel: timeframes.find(t => t.value === tf)?.label || tf, + success: true, + result: { + analysis: data.analysis, // Single comparative analysis for all timeframes + screenshots: data.screenshots.filter((s: string) => s.includes(`_${tf}_`)), + sessionId: sessionId + } + })) } - + setResult(multiResult) - // Call the callback with the first successful analysis result if provided - if (onAnalysisComplete) { - const firstSuccessfulResult = results.find(r => r.success && r.result?.analysis) - if (firstSuccessfulResult) { - onAnalysisComplete(firstSuccessfulResult.result.analysis, analysisSymbol) - } + // Call the callback with analysis result if provided + if (onAnalysisComplete && data.analysis) { + onAnalysisComplete(data.analysis, analysisSymbol) } } } catch (err) { @@ -967,7 +959,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP )} - {result && result.type === 'multi_timeframe' && ( + {result && (result.type === 'multi_timeframe' || result.type === 'batch_comparative') && (