Implement batch comparative analysis for multi-timeframe
- Create new /api/batch-analysis endpoint for collecting all screenshots first - Modified AIAnalysisPanel to use batch analysis for multiple timeframes - All screenshots captured before AI analysis (no immediate analysis per timeframe) - Enables AI to compare across timeframes for better trend analysis - Maintains UI compatibility with existing display logic
This commit is contained in:
185
app/api/batch-analysis/route.js
Normal file
185
app/api/batch-analysis/route.js
Normal file
@@ -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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -306,68 +306,60 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
onAnalysisComplete(data.analysis, analysisSymbol)
|
onAnalysisComplete(data.analysis, analysisSymbol)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Multiple timeframe analysis
|
// Multiple timeframe analysis - use batch comparative analysis
|
||||||
const results = []
|
console.log(`🎯 Starting batch comparative analysis for ${analysisTimeframes.length} timeframes`)
|
||||||
|
|
||||||
for (let i = 0; i < analysisTimeframes.length; i++) {
|
// Pre-generate sessionId for batch analysis
|
||||||
const tf = analysisTimeframes[i]
|
const sessionId = `batch_analysis_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||||
const timeframeLabel = timeframes.find(t => t.value === tf)?.label || tf
|
startProgressTracking(sessionId)
|
||||||
|
|
||||||
console.log(`🧪 Analyzing timeframe: ${timeframeLabel}`)
|
const response = await fetch('/api/batch-analysis', {
|
||||||
|
|
||||||
const response = await fetch('/api/enhanced-screenshot', {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
symbol: analysisSymbol,
|
symbol: analysisSymbol,
|
||||||
timeframe: tf,
|
timeframes: analysisTimeframes,
|
||||||
layouts: selectedLayouts,
|
layouts: selectedLayouts,
|
||||||
analyze: true
|
sessionId: sessionId
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await response.json()
|
const data = await response.json()
|
||||||
results.push({
|
|
||||||
timeframe: tf,
|
|
||||||
timeframeLabel,
|
|
||||||
success: response.ok,
|
|
||||||
result
|
|
||||||
})
|
|
||||||
|
|
||||||
// Start progress tracking for the first timeframe session
|
if (!response.ok) {
|
||||||
if (i === 0 && result.sessionId) {
|
throw new Error(data.error || 'Batch analysis failed')
|
||||||
startProgressTracking(result.sessionId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update timeframe progress manually for multi-timeframe
|
console.log(`✅ Batch analysis completed: ${data.totalScreenshots} screenshots, ${data.timeframes.length} timeframes`)
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Transform batch result to match expected format for UI compatibility
|
||||||
const multiResult = {
|
const multiResult = {
|
||||||
type: 'multi_timeframe',
|
type: 'batch_comparative',
|
||||||
symbol: analysisSymbol,
|
symbol: analysisSymbol,
|
||||||
summary: `Analyzed ${results.length} timeframes for ${analysisSymbol}`,
|
summary: data.summary || `Batch comparative analysis of ${data.timeframes?.length || analysisTimeframes.length} timeframes`,
|
||||||
results
|
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)
|
setResult(multiResult)
|
||||||
|
|
||||||
// Call the callback with the first successful analysis result if provided
|
// Call the callback with analysis result if provided
|
||||||
if (onAnalysisComplete) {
|
if (onAnalysisComplete && data.analysis) {
|
||||||
const firstSuccessfulResult = results.find(r => r.success && r.result?.analysis)
|
onAnalysisComplete(data.analysis, analysisSymbol)
|
||||||
if (firstSuccessfulResult) {
|
|
||||||
onAnalysisComplete(firstSuccessfulResult.result.analysis, analysisSymbol)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -967,7 +959,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{result && result.type === 'multi_timeframe' && (
|
{result && (result.type === 'multi_timeframe' || result.type === 'batch_comparative') && (
|
||||||
<div className="mt-6 space-y-4">
|
<div className="mt-6 space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="text-lg font-bold text-white flex items-center">
|
<h3 className="text-lg font-bold text-white flex items-center">
|
||||||
@@ -1482,7 +1474,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Multi-timeframe Screenshot Gallery */}
|
{/* Multi-timeframe Screenshot Gallery */}
|
||||||
{result && result.type === 'multi_timeframe' && result.results && (
|
{result && (result.type === 'multi_timeframe' || result.type === 'batch_comparative') && result.results && (
|
||||||
<ScreenshotGallery
|
<ScreenshotGallery
|
||||||
screenshots={result.results
|
screenshots={result.results
|
||||||
.filter((r: any) => r.success && r.result.screenshots)
|
.filter((r: any) => r.success && r.result.screenshots)
|
||||||
|
|||||||
Reference in New Issue
Block a user