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:')