diff --git a/app/api/enhanced-screenshot/route.ts b/app/api/enhanced-screenshot/route.ts
index 0692726..b5d47e0 100644
--- a/app/api/enhanced-screenshot/route.ts
+++ b/app/api/enhanced-screenshot/route.ts
@@ -1,15 +1,16 @@
import { NextRequest, NextResponse } from 'next/server'
import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot-simple'
+import { AIAnalysisService } from '../../../lib/ai-analysis'
export async function POST(req: NextRequest) {
try {
- const { symbol, timeframe, layouts, credentials } = await req.json()
+ const { symbol, timeframe, layouts, credentials, analyze = false } = await req.json()
if (!symbol) {
return NextResponse.json({ error: 'Missing symbol' }, { status: 400 })
}
- console.log('Enhanced screenshot API called with:', { symbol, timeframe, layouts })
+ console.log('Enhanced screenshot API called with:', { symbol, timeframe, layouts, analyze })
const config = {
symbol,
@@ -20,11 +21,48 @@ export async function POST(req: NextRequest) {
const screenshots = await enhancedScreenshotService.captureWithLogin(config)
- return NextResponse.json({
+ let analysis = null
+ if (analyze && screenshots.length > 0) {
+ console.log('š¤ Starting AI analysis of screenshots...')
+ try {
+ const aiAnalysisService = new AIAnalysisService()
+
+ // Extract filenames from screenshot paths for analysis
+ const filenames = screenshots.map(path => path.split('/').pop() || '').filter(Boolean)
+
+ if (filenames.length > 0) {
+ console.log(`š Analyzing ${filenames.length} screenshots: ${filenames.join(', ')}`)
+
+ if (filenames.length === 1) {
+ // Single screenshot analysis
+ analysis = await aiAnalysisService.analyzeScreenshot(filenames[0])
+ } else {
+ // Multi-screenshot analysis for comprehensive trading advice
+ analysis = await aiAnalysisService.analyzeMultipleScreenshots(filenames)
+ }
+
+ console.log('ā
AI analysis completed:', analysis ? 'Success' : 'Failed')
+ } else {
+ console.warn('ā ļø No valid screenshot filenames found for analysis')
+ }
+ } catch (analysisError: any) {
+ console.error('ā AI analysis failed:', analysisError.message)
+ // Don't fail the whole request if analysis fails
+ }
+ }
+
+ const response = {
success: true,
screenshots,
+ analysis,
message: `Captured ${screenshots.length} screenshot(s) for ${symbol} with layouts: ${layouts?.join(', ') || 'default'}`
- })
+ }
+
+ if (analysis) {
+ response.message += '. AI analysis completed.'
+ }
+
+ return NextResponse.json(response)
} catch (error: any) {
console.error('Enhanced screenshot API error:', error)
diff --git a/components/AIAnalysisPanel.tsx b/components/AIAnalysisPanel.tsx
index 0451a10..5530b15 100644
--- a/components/AIAnalysisPanel.tsx
+++ b/components/AIAnalysisPanel.tsx
@@ -65,7 +65,8 @@ export default function AIAnalysisPanel() {
body: JSON.stringify({
symbol: analysisSymbol,
timeframe: analysisTimeframe,
- layouts: selectedLayouts
+ layouts: selectedLayouts,
+ analyze: true // Request AI analysis of captured screenshots
})
})
@@ -115,7 +116,8 @@ export default function AIAnalysisPanel() {
body: JSON.stringify({
symbol,
timeframe: tf.value,
- layouts: selectedLayouts
+ layouts: selectedLayouts,
+ analyze: true // Request AI analysis for timeframe testing too
})
})
@@ -352,7 +354,7 @@ export default function AIAnalysisPanel() {
)}
- {result && (
+ {result && result.analysis && (
@@ -361,9 +363,9 @@ export default function AIAnalysisPanel() {
Analysis Complete
- {result.layoutsAnalyzed && (
+ {result.screenshots && (
- Layouts: {result.layoutsAnalyzed.join(', ')}
+ Screenshots: {result.screenshots.length} captured
)}
@@ -375,86 +377,149 @@ export default function AIAnalysisPanel() {
Market Summary
-
{safeRender(result.summary)}
+
{safeRender(result.analysis.summary)}
{/* Key Metrics */}
Market Sentiment
-
{safeRender(result.marketSentiment)}
+
{safeRender(result.analysis.marketSentiment)}
Recommendation
-
{safeRender(result.recommendation)}
- {result.confidence && (
-
{safeRender(result.confidence)}% confidence
+
{safeRender(result.analysis.recommendation)}
+ {result.analysis.confidence && (
+
{safeRender(result.analysis.confidence)}% confidence
)}
{/* Trading Levels */}
- {result.keyLevels && (
+ {result.analysis.keyLevels && (
Resistance Levels
- {result.keyLevels.resistance?.join(', ') || 'None identified'}
+ {result.analysis.keyLevels.resistance?.join(', ') || 'None identified'}
Support Levels
- {result.keyLevels.support?.join(', ') || 'None identified'}
+ {result.analysis.keyLevels.support?.join(', ') || 'None identified'}
)}
{/* Trading Setup */}
- {(result.entry || result.stopLoss || result.takeProfits) && (
+ {(result.analysis.entry || result.analysis.stopLoss || result.analysis.takeProfits) && (
Trading Setup
- {result.entry && (
+ {result.analysis.entry && (
Entry Point
- ${safeRender(result.entry.price || result.entry)}
+ ${safeRender(result.analysis.entry.price || result.analysis.entry)}
- {result.entry.rationale && (
-
{safeRender(result.entry.rationale)}
+ {result.analysis.entry.rationale && (
+
{safeRender(result.analysis.entry.rationale)}
)}
)}
- {result.stopLoss && (
+ {result.analysis.stopLoss && (
Stop Loss
- ${safeRender(result.stopLoss.price || result.stopLoss)}
+ ${safeRender(result.analysis.stopLoss.price || result.analysis.stopLoss)}
- {result.stopLoss.rationale && (
-
{safeRender(result.stopLoss.rationale)}
+ {result.analysis.stopLoss.rationale && (
+
{safeRender(result.analysis.stopLoss.rationale)}
)}
)}
- {result.takeProfits && (
+ {result.analysis.takeProfits && (
Take Profit
- {typeof result.takeProfits === 'object'
- ? Object.values(result.takeProfits).map(tp => `$${safeRender(tp)}`).join(', ')
- : `$${safeRender(result.takeProfits)}`}
+ {typeof result.analysis.takeProfits === 'object'
+ ? Object.values(result.analysis.takeProfits).map(tp => `$${safeRender(tp)}`).join(', ')
+ : `$${safeRender(result.analysis.takeProfits)}`}
)}
)}
+
+ {/* Layout Comparison Section */}
+ {result.analysis.layoutComparison && (
+
+
Multi-Layout Analysis
+
+ {result.analysis.layoutComparison.aiLayout && (
+
+
AI Layout Insights
+
{result.analysis.layoutComparison.aiLayout}
+
+ )}
+
+ {result.analysis.layoutComparison.diyLayout && (
+
+
DIY Layout Insights
+
{result.analysis.layoutComparison.diyLayout}
+
+ )}
+
+
+ {result.analysis.layoutComparison.consensus && (
+
+
Layout Consensus
+
{result.analysis.layoutComparison.consensus}
+
+ )}
+
+ {result.analysis.layoutComparison.divergences && (
+
+
Layout Divergences
+
{result.analysis.layoutComparison.divergences}
+
+ )}
+
+ )}
+
+ {/* Enhanced Indicator Analysis */}
+ {result.analysis.indicatorAnalysis?.crossLayoutConsensus && (
+
+
Cross-Layout Consensus
+
{result.analysis.indicatorAnalysis.crossLayoutConsensus}
+
+ )}
+
+
+ )}
+
+ {result && !result.analysis && result.screenshots && (
+
+
Screenshots Captured
+
+ Screenshots were captured successfully, but AI analysis failed or was not requested.
+
+
+ Screenshots: {result.screenshots.length} captured
+
+
+ {result.screenshots.map((screenshot: string, index: number) => (
+
+ {screenshot.split('/').pop()}
+
+ ))}
)}
diff --git a/lib/ai-analysis.ts b/lib/ai-analysis.ts
index 024adfe..bb5d95e 100644
--- a/lib/ai-analysis.ts
+++ b/lib/ai-analysis.ts
@@ -98,7 +98,7 @@ Provide your analysis in this exact JSON format (replace values with your analys
Return only the JSON object with your technical analysis.`
const response = await openai.chat.completions.create({
- model: "gpt-4o", // Updated to current vision model
+ model: "gpt-4o-mini", // Cost-effective vision model
messages: [
{
role: "user",
@@ -173,126 +173,137 @@ Return only the JSON object with your technical analysis.`
async analyzeMultipleScreenshots(filenames: string[]): Promise {
try {
const screenshotsDir = path.join(process.cwd(), 'screenshots')
- const images: any[] = []
- for (const filename of filenames) {
- const imagePath = path.join(screenshotsDir, filename)
- const imageBuffer = await fs.readFile(imagePath)
- const base64Image = imageBuffer.toString('base64')
- images.push({ type: "image_url", image_url: { url: `data:image/png;base64,${base64Image}` } })
- }
-
- const prompt = `You are a technical chart analysis expert. Please analyze these TradingView chart images and provide objective technical analysis data.
+ // Read all image files and convert to base64
+ const images = await Promise.all(
+ filenames.map(async (filename) => {
+ const imagePath = path.join(screenshotsDir, filename)
+ const imageBuffer = await fs.readFile(imagePath)
+ const base64Image = imageBuffer.toString('base64')
+ return {
+ type: "image_url" as const,
+ image_url: {
+ url: `data:image/png;base64,${base64Image}`,
+ detail: "high" as const
+ }
+ }
+ })
+ )
-**Important**: This is for educational and research purposes only. Please analyze the technical indicators, price levels, and chart patterns visible in the images.
+ const layoutInfo = filenames.map(f => {
+ if (f.includes('_ai_')) return 'AI Layout'
+ if (f.includes('_diy_') || f.includes('_Diy module_')) return 'DIY Module Layout'
+ return 'Unknown Layout'
+ }).join(' and ')
-Examine all the charts and provide a consolidated analysis by identifying:
-- Current price action and trend direction across layouts
-- Key support and resistance levels visible on the charts
-- Technical indicator readings (RSI, moving averages, volume if visible)
-- Chart patterns or formations
-- Market structure elements
-- Cross-reference different timeframes/layouts for the most accurate analysis
+ const prompt = `You are a professional technical chart analysis expert. I'm providing you with ${filenames.length} TradingView chart screenshots from different layouts: ${layoutInfo}.
-**CRITICAL: You MUST analyze the actual chart images provided. Do not respond with generic advice.**
+**IMPORTANT**: Please analyze ALL screenshots together to provide comprehensive trading advice. Compare the different layouts and use ALL available information to make the most informed recommendation.
-Provide your analysis in this exact JSON format (replace values with your analysis):
+**Analysis Requirements:**
+1. Examine ALL charts for:
+ - Current price action and trend direction across all timeframes/layouts
+ - Key support and resistance levels visible in any chart
+ - Technical indicator readings (RSI, moving averages, volume, etc.)
+ - Chart patterns or formations
+ - Market structure elements
+ - Any divergences or confirmations between the layouts
+
+2. **Cross-Layout Analysis**:
+ - Compare insights from different chart layouts
+ - Look for confirmations or contradictions between views
+ - Identify which layout provides the clearest signals
+ - Use multiple perspectives to increase confidence
+
+3. **Comprehensive Trading Setup**:
+ - Provide entry, stop loss, and take profit levels
+ - Include risk-to-reward analysis
+ - Suggest confirmation triggers
+ - Rate your confidence based on multi-layout consensus
+
+**Response Format** (return only valid JSON):
{
- "summary": "Objective description combining analysis from all charts",
+ "summary": "Comprehensive analysis of all charts including cross-layout insights and consensus",
"marketSentiment": "BULLISH|BEARISH|NEUTRAL",
"keyLevels": {
- "support": [list of visible support price levels as numbers],
- "resistance": [list of visible resistance price levels as numbers]
+ "support": [array of support levels from all charts],
+ "resistance": [array of resistance levels from all charts]
},
"recommendation": "BUY|SELL|HOLD",
- "confidence": 75,
- "reasoning": "Technical analysis reasoning based on indicators and price action from all layouts",
+ "confidence": 85,
+ "reasoning": "Multi-layout technical analysis reasoning explaining how different charts confirm or contradict each other",
"entry": {
"price": 150.50,
"buffer": "±0.25",
- "rationale": "Technical reasoning for entry level"
+ "rationale": "Entry reasoning based on multiple chart analysis"
},
"stopLoss": {
"price": 148.00,
- "rationale": "Technical reasoning for stop level"
+ "rationale": "Stop loss reasoning considering all layouts"
},
"takeProfits": {
- "tp1": { "price": 152.00, "description": "First target reasoning" },
- "tp2": { "price": 154.00, "description": "Second target reasoning" }
+ "tp1": { "price": 152.00, "description": "First target based on multi-chart analysis" },
+ "tp2": { "price": 154.00, "description": "Second target with cross-layout confirmation" }
},
- "riskToReward": "1:2",
- "confirmationTrigger": "Technical signal to watch for",
+ "riskToReward": "1:2.5",
+ "confirmationTrigger": "Multi-layout signal confirmation to watch for",
"indicatorAnalysis": {
- "rsi": "RSI level and interpretation",
- "vwap": "VWAP relationship to price",
- "obv": "Volume analysis if visible"
+ "rsi": "RSI analysis across layouts",
+ "vwap": "VWAP analysis from multiple views",
+ "obv": "Volume analysis synthesis",
+ "crossLayoutConsensus": "How well different layouts agree on the signals"
+ },
+ "layoutComparison": {
+ "aiLayout": "Insights specific to AI layout",
+ "diyLayout": "Insights specific to DIY module layout",
+ "consensus": "Areas where both layouts agree",
+ "divergences": "Areas where layouts show different signals"
}
}
-Return only the JSON object with your consolidated technical analysis.`
+Analyze all provided screenshots comprehensively and return only the JSON response.`
+ const messages = [
+ {
+ role: "user" as const,
+ content: [
+ { type: "text" as const, text: prompt },
+ ...images
+ ]
+ }
+ ]
+
+ console.log(`š¤ Sending ${filenames.length} screenshots to OpenAI for multi-layout analysis...`)
+
const response = await openai.chat.completions.create({
- model: "gpt-4o", // gpt-4o has better vision capabilities than gpt-4-vision-preview
- messages: [
- {
- role: "user",
- content: [
- { type: "text", text: prompt },
- ...images
- ]
- }
- ],
- max_tokens: 2000, // Increased for more detailed analysis
+ model: "gpt-4o-mini", // Cost-effective model with vision capabilities
+ messages,
+ max_tokens: 2000,
temperature: 0.1
})
const content = response.choices[0]?.message?.content
if (!content) {
- throw new Error('No content received from OpenAI')
+ throw new Error('No response from OpenAI')
}
- console.log('AI response content:', content)
+ console.log('š Raw OpenAI response:', content.substring(0, 200) + '...')
- // Parse the JSON response
+ // Parse JSON response
const jsonMatch = content.match(/\{[\s\S]*\}/)
if (!jsonMatch) {
- console.error('No JSON found in response. Full content:', content)
throw new Error('No JSON found in response')
}
- console.log('Extracted JSON:', jsonMatch[0])
-
const analysis = JSON.parse(jsonMatch[0])
+ console.log('ā
Multi-layout analysis parsed successfully')
- // Sanitize the analysis result to ensure no nested objects cause React issues
- const sanitizedAnalysis = {
- summary: typeof analysis.summary === 'string' ? analysis.summary : String(analysis.summary || ''),
- marketSentiment: analysis.marketSentiment || 'NEUTRAL',
- keyLevels: {
- support: Array.isArray(analysis.keyLevels?.support) ? analysis.keyLevels.support : [],
- resistance: Array.isArray(analysis.keyLevels?.resistance) ? analysis.keyLevels.resistance : []
- },
- recommendation: analysis.recommendation || 'HOLD',
- confidence: typeof analysis.confidence === 'number' ? analysis.confidence : 0,
- reasoning: typeof analysis.reasoning === 'string' ? analysis.reasoning : String(analysis.reasoning || ''),
- ...(analysis.entry && { entry: analysis.entry }),
- ...(analysis.stopLoss && { stopLoss: analysis.stopLoss }),
- ...(analysis.takeProfits && { takeProfits: analysis.takeProfits }),
- ...(analysis.riskToReward && { riskToReward: String(analysis.riskToReward) }),
- ...(analysis.confirmationTrigger && { confirmationTrigger: String(analysis.confirmationTrigger) }),
- ...(analysis.indicatorAnalysis && { indicatorAnalysis: analysis.indicatorAnalysis })
- }
-
- // Validate the structure
- if (!sanitizedAnalysis.summary || !sanitizedAnalysis.marketSentiment || !sanitizedAnalysis.recommendation || typeof sanitizedAnalysis.confidence !== 'number') {
- console.error('Invalid analysis structure:', sanitizedAnalysis)
- throw new Error('Invalid analysis structure')
- }
+ return analysis as AnalysisResult
- return sanitizedAnalysis
- } catch (error) {
- console.error('AI multi-analysis error:', error)
+ } catch (error: any) {
+ console.error('ā Multi-screenshot AI analysis failed:', error.message)
+ console.error('Full error:', error)
return null
}
}
diff --git a/test-enhanced-screenshot.js b/test-enhanced-screenshot.js
index c37a8f3..3bd28a0 100644
--- a/test-enhanced-screenshot.js
+++ b/test-enhanced-screenshot.js
@@ -13,7 +13,8 @@ async function testDualSessionScreenshots() {
const config = {
symbol: 'SOLUSD',
timeframe: '240',
- layouts: ['ai', 'diy']
+ layouts: ['ai', 'diy'],
+ analyze: true // Request AI analysis of both screenshots
}
console.log('š Test Configuration:', config)
@@ -70,6 +71,29 @@ async function testDualSessionScreenshots() {
})
}
}
+
+ // Display AI analysis results if available
+ if (result.analysis) {
+ console.log('\nš¤ AI Analysis Results:')
+ console.log(` š Market Sentiment: ${result.analysis.marketSentiment}`)
+ console.log(` š Recommendation: ${result.analysis.recommendation}`)
+ console.log(` šÆ Confidence: ${result.analysis.confidence}%`)
+ if (result.analysis.entry) {
+ console.log(` š° Entry: $${result.analysis.entry.price}`)
+ }
+ if (result.analysis.keyLevels) {
+ console.log(` š“ Resistance: ${result.analysis.keyLevels.resistance?.join(', ') || 'None'}`)
+ console.log(` š¢ Support: ${result.analysis.keyLevels.support?.join(', ') || 'None'}`)
+ }
+ if (result.analysis.layoutComparison) {
+ console.log('\nš Layout Comparison:')
+ console.log(` š AI Layout: ${result.analysis.layoutComparison.aiLayout}`)
+ console.log(` š§ DIY Layout: ${result.analysis.layoutComparison.diyLayout}`)
+ console.log(` ā
Consensus: ${result.analysis.layoutComparison.consensus}`)
+ }
+ } else {
+ console.log('\nā ļø No AI analysis results received')
+ }
// Test summary
console.log('\nš Test Summary:')