diff --git a/app/api/analyze/route.ts b/app/api/analyze/route.ts index bfd66a2..9069619 100644 --- a/app/api/analyze/route.ts +++ b/app/api/analyze/route.ts @@ -3,10 +3,11 @@ import { aiAnalysisService } from '../../../lib/ai-analysis' import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot' import { settingsManager } from '../../../lib/settings' import path from 'path' +import fs from 'fs' export async function POST(req: NextRequest) { try { - const { symbol, layouts, timeframe } = await req.json() + const { symbol, layouts, timeframe, useExisting } = await req.json() // Load current settings const settings = await settingsManager.loadSettings() @@ -19,9 +20,37 @@ export async function POST(req: NextRequest) { if (!finalSymbol) { return NextResponse.json({ error: 'Missing symbol' }, { status: 400 }) } + + let screenshots: string[] = [] - const baseFilename = `${finalSymbol}_${finalTimeframe}_${Date.now()}` - const screenshots = await enhancedScreenshotService.capture(finalSymbol, `${baseFilename}.png`, finalLayouts, finalTimeframe) + // If useExisting is true, find existing screenshots + if (useExisting) { + console.log('Using existing screenshots for analysis...') + const screenshotsDir = path.join(process.cwd(), 'screenshots') + const allFiles = await fs.promises.readdir(screenshotsDir) + + // Find screenshots matching the symbol and timeframe + const matchingFiles = allFiles.filter(file => + file.includes(finalSymbol) && + file.includes(finalTimeframe) && + file.endsWith('.png') && + !file.includes('debug') + ) + + if (matchingFiles.length > 0) { + // Use the most recent screenshots (limit to 3 for analysis) + screenshots = matchingFiles + .sort((a, b) => b.localeCompare(a)) // Sort by name (which includes timestamp) + .slice(0, 3) + .map(file => path.join(screenshotsDir, file)) + } else { + return NextResponse.json({ error: `No existing screenshots found for ${finalSymbol} ${finalTimeframe}` }, { status: 404 }) + } + } else { + // Original behavior - capture new screenshots + const baseFilename = `${finalSymbol}_${finalTimeframe}_${Date.now()}` + screenshots = await enhancedScreenshotService.capture(finalSymbol, `${baseFilename}.png`, finalLayouts, finalTimeframe) + } let result if (screenshots.length === 1) { @@ -46,7 +75,8 @@ export async function POST(req: NextRequest) { timeframe: finalTimeframe, layouts: finalLayouts }, - screenshots: screenshots.map((s: string) => path.basename(s)) + screenshots: screenshots.map((s: string) => path.basename(s)), + usedExisting: useExisting || false }) } catch (e: any) { return NextResponse.json({ error: e.message }, { status: 500 }) diff --git a/debug-ai-analysis.js b/debug-ai-analysis.js new file mode 100644 index 0000000..a5b7bef --- /dev/null +++ b/debug-ai-analysis.js @@ -0,0 +1,113 @@ +const fs = require('fs') +const path = require('path') +const OpenAI = require('openai').default + +// Initialize OpenAI +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}) + +async function testAIAnalysis() { + try { + console.log('๐Ÿ” Testing AI analysis with a real screenshot...') + + // Use one of the existing screenshots + const screenshotsDir = path.join(process.cwd(), 'screenshots') + const testImage = 'SOLUSD_60_1752324455391_ai.png' + const imagePath = path.join(screenshotsDir, testImage) + + console.log(`๐Ÿ“ธ Using screenshot: ${imagePath}`) + + // Check if file exists + if (!fs.existsSync(imagePath)) { + console.error('โŒ Screenshot file not found!') + return + } + + console.log('โœ… Screenshot file found') + + // Read and encode image + const imageBuffer = fs.readFileSync(imagePath) + const base64Image = imageBuffer.toString('base64') + + console.log(`๐Ÿ“Š Image size: ${imageBuffer.length} bytes`) + console.log(`๐Ÿ”’ Base64 length: ${base64Image.length} characters`) + console.log(`๐ŸŽฏ Base64 preview: ${base64Image.substring(0, 100)}...`) + + // Improved prompt for testing + const prompt = `You are a technical chart analysis expert. Please analyze this TradingView chart image and provide objective technical analysis data. + +**Important**: This is for educational and research purposes only. Please analyze the technical indicators, price levels, and chart patterns visible in the image. + +Examine the chart and identify: +- Current price action and trend direction +- Key support and resistance levels visible on the chart +- Technical indicator readings (RSI, moving averages, volume if visible) +- Chart patterns or formations +- Market structure elements + +Provide your analysis in this exact JSON format (replace values with your analysis): + +{ + "summary": "Objective description of what you observe in the chart", + "marketSentiment": "BULLISH", + "recommendation": "BUY", + "confidence": 75 +} + +Return only the JSON object with your technical analysis.` + + console.log('๐Ÿค– Sending request to OpenAI...') + + const response = await openai.chat.completions.create({ + model: "gpt-4o", // Latest vision model + messages: [ + { + role: "user", + content: [ + { type: "text", text: prompt }, + { + type: "image_url", + image_url: { + url: `data:image/png;base64,${base64Image}`, + detail: "low" // Add detail parameter to reduce token usage + } + } + ] + } + ], + max_tokens: 500, + temperature: 0.1 + }) + + console.log('โœ… Response received from OpenAI') + + const content = response.choices[0]?.message?.content + console.log('๐Ÿ“ Full response content:') + console.log(content) + + // Try to extract JSON + const jsonMatch = content?.match(/\{[\s\S]*\}/) + if (jsonMatch) { + console.log('โœ… JSON found in response:') + console.log(jsonMatch[0]) + + try { + const parsed = JSON.parse(jsonMatch[0]) + console.log('โœ… JSON successfully parsed:') + console.log(JSON.stringify(parsed, null, 2)) + } catch (e) { + console.error('โŒ Failed to parse JSON:', e.message) + } + } else { + console.error('โŒ No JSON found in response') + } + + } catch (error) { + console.error('โŒ Error testing AI analysis:', error.message) + console.error('Full error:', error) + } +} + +// Run the test +testAIAnalysis() diff --git a/debug-openai-vision.js b/debug-openai-vision.js new file mode 100644 index 0000000..d14023c --- /dev/null +++ b/debug-openai-vision.js @@ -0,0 +1,71 @@ +const OpenAI = require('openai').default + +async function testOpenAIVision() { + try { + console.log('๐Ÿ” Testing OpenAI Vision capabilities...') + + const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, + }) + + // Test with a simple text-only request first + console.log('๐Ÿ“ Testing text-only request...') + + const textResponse = await openai.chat.completions.create({ + model: "gpt-4o", + messages: [ + { + role: "user", + content: "Respond with a simple JSON: {'test': 'success'}" + } + ], + max_tokens: 50 + }) + + console.log('โœ… Text response:', textResponse.choices[0]?.message?.content) + + // Test with a simple image URL (public image) + console.log('๐Ÿ–ผ๏ธ Testing with public image...') + + const imageResponse = await openai.chat.completions.create({ + model: "gpt-4o", + messages: [ + { + role: "user", + content: [ + { type: "text", text: "What do you see in this image? Respond with JSON: {'description': 'your description'}" }, + { + type: "image_url", + image_url: { + url: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" + } + } + ] + } + ], + max_tokens: 100 + }) + + console.log('โœ… Image response:', imageResponse.choices[0]?.message?.content) + + // Check account details + console.log('๐Ÿ” Checking available models...') + + const models = await openai.models.list() + const visionModels = models.data.filter(model => + model.id.includes('gpt-4') && (model.id.includes('vision') || model.id.includes('gpt-4o')) + ) + + console.log('๐ŸŽฏ Available vision models:') + visionModels.forEach(model => console.log(` - ${model.id}`)) + + } catch (error) { + console.error('โŒ Error:', error.message) + if (error.response) { + console.error('๐Ÿ“Š Response status:', error.response.status) + console.error('๐Ÿ“ Response data:', JSON.stringify(error.response.data, null, 2)) + } + } +} + +testOpenAIVision() diff --git a/lib/ai-analysis.ts b/lib/ai-analysis.ts index 980a5cd..7ea8f55 100644 --- a/lib/ai-analysis.ts +++ b/lib/ai-analysis.ts @@ -50,73 +50,53 @@ export class AIAnalysisService { const imageBuffer = await fs.readFile(imagePath) const base64Image = imageBuffer.toString('base64') - const prompt = `You are now a professional trading assistant focused on short-term crypto trading using 5โ€“15min timeframes. You behave with the precision and decisiveness of a top proprietary desk trader. No vagueness, no fluff. + const prompt = `You are a technical chart analysis expert. Please analyze this TradingView chart image and provide objective technical analysis data. -Analyze the attached TradingView chart screenshot and provide a detailed trading analysis. +**Important**: This is for educational and research purposes only. Please analyze the technical indicators, price levels, and chart patterns visible in the image. -### WHEN GIVING A TRADE SETUP: -Be 100% SPECIFIC. Provide: +Examine the chart and identify: +- Current price action and trend direction +- Key support and resistance levels visible on the chart +- Technical indicator readings (RSI, moving averages, volume if visible) +- Chart patterns or formations +- Market structure elements -1. **ENTRY** - - Exact price level (with a ยฑ entry buffer if needed) - - Rationale: e.g., "Rejection from 15 EMA + VWAP confluence near intraday supply" +Provide your analysis in this exact JSON format (replace values with your analysis): -2. **STOP-LOSS (SL)** - - Exact level (not arbitrary) - - Explain *why* it's there: "Above VWAP + failed breakout zone" - -3. **TAKE PROFITS** - - TP1: Immediate structure (ex: previous low at $149.20) - - TP2: Extended target if momentum continues (e.g., $148.00) - - Mention **expected RSI/OBV behavior** at each TP zone - -4. **RISK-TO-REWARD** - - Show R:R. Ex: "1:2.5 โ€” Risking $X to potentially gain $Y" - -5. **CONFIRMATION TRIGGER** - - Exact signal to wait for: e.g., "Bearish engulfing candle on rejection from VWAP zone" - - OBV: "Must be making lower highs + dropping below 30min average" - - RSI: "Should remain under 50 on rejection. Overbought โ‰ฅ70 = wait" - -6. **INDICATOR ANALYSIS** - - **RSI**: If RSI crosses above 70 while price is under resistance โ†’ *wait* - - **VWAP**: If price retakes VWAP with bullish momentum โ†’ *consider invalidation* - - **OBV**: If OBV starts climbing while price stays flat โ†’ *early exit or reconsider bias* - -Return your answer as a JSON object with the following structure: { - "summary": "Brief market summary", - "marketSentiment": "BULLISH" | "BEARISH" | "NEUTRAL", + "summary": "Objective description of what you observe in the chart", + "marketSentiment": "BULLISH|BEARISH|NEUTRAL", "keyLevels": { - "support": [number array], - "resistance": [number array] + "support": [list of visible support price levels as numbers], + "resistance": [list of visible resistance price levels as numbers] }, - "recommendation": "BUY" | "SELL" | "HOLD", - "confidence": number (0-100), - "reasoning": "Detailed reasoning with specific levels, indicators, and confirmation triggers", + "recommendation": "BUY|SELL|HOLD", + "confidence": 75, + "reasoning": "Technical analysis reasoning based on indicators and price action", "entry": { - "price": number, - "buffer": "string describing entry buffer", - "rationale": "string explaining entry logic" + "price": 150.50, + "buffer": "ยฑ0.25", + "rationale": "Technical reasoning for entry level" }, "stopLoss": { - "price": number, - "rationale": "string explaining stop loss placement" + "price": 148.00, + "rationale": "Technical reasoning for stop level" }, "takeProfits": { - "tp1": { "price": number, "description": "string" }, - "tp2": { "price": number, "description": "string" } + "tp1": { "price": 152.00, "description": "First target reasoning" }, + "tp2": { "price": 154.00, "description": "Second target reasoning" } }, - "riskToReward": "string like '1:2.5 - Risking $X to gain $Y'", - "confirmationTrigger": "string describing exact signal to wait for", + "riskToReward": "1:2", + "confirmationTrigger": "Technical signal to watch for", "indicatorAnalysis": { - "rsi": "string describing RSI behavior", - "vwap": "string describing VWAP behavior", - "obv": "string describing OBV behavior" + "rsi": "RSI level and interpretation", + "vwap": "VWAP relationship to price", + "obv": "Volume analysis if visible" } } -Be concise but thorough. Only return valid JSON.` +Return only the JSON object with your technical analysis.` + const response = await openai.chat.completions.create({ model: "gpt-4o", // Updated to current vision model messages: [ @@ -124,17 +104,31 @@ Be concise but thorough. Only return valid JSON.` role: "user", content: [ { type: "text", text: prompt }, - { type: "image_url", image_url: { url: `data:image/png;base64,${base64Image}` } } + { + type: "image_url", + image_url: { + url: `data:image/png;base64,${base64Image}`, + detail: "low" // Reduce token usage + } + } ] } ], - max_tokens: 1024 + max_tokens: 1024, + temperature: 0.1 }) + const content = response.choices[0]?.message?.content if (!content) return null + + console.log('AI response content:', content) + // Extract JSON from response const match = content.match(/\{[\s\S]*\}/) - if (!match) return null + if (!match) { + console.error('No JSON found in response. Full content:', content) + return null + } const json = match[0] console.log('Raw JSON from AI:', json) @@ -181,83 +175,55 @@ Be concise but thorough. Only return valid JSON.` images.push({ type: "image_url", image_url: { url: `data:image/png;base64,${base64Image}` } }) } - const prompt = `You are an expert crypto trading analyst with advanced vision capabilities. I'm sending you TradingView chart screenshot(s) that you CAN and MUST analyze. + const prompt = `You are a technical chart analysis expert. Please analyze these TradingView chart images and provide objective technical analysis data. -**IMPORTANT: You have full image analysis capabilities. Please analyze the TradingView chart images I'm providing.** +**Important**: This is for educational and research purposes only. Please analyze the technical indicators, price levels, and chart patterns visible in the images. -Analyze the attached TradingView chart screenshots (multiple layouts of the same symbol) and provide a comprehensive trading analysis by combining insights from all charts. - -### TRADING ANALYSIS REQUIREMENTS: - -You are a professional trading assistant focused on short-term crypto trading using 5โ€“15min timeframes. You behave with the precision and decisiveness of a top proprietary desk trader. No vagueness, no fluff. - -### WHEN GIVING A TRADE SETUP: -Be 100% SPECIFIC. Provide: - -1. **ENTRY** - - Exact price level (with a ยฑ entry buffer if needed) - - Rationale: e.g., "Rejection from 15 EMA + VWAP confluence near intraday supply" - -2. **STOP-LOSS (SL)** - - Exact level (not arbitrary) - - Explain *why* it's there: "Above VWAP + failed breakout zone" - -3. **TAKE PROFITS** - - TP1: Immediate structure (ex: previous low at $149.20) - - TP2: Extended target if momentum continues (e.g., $148.00) - - Mention **expected RSI/OBV behavior** at each TP zone - -4. **RISK-TO-REWARD** - - Show R:R. Ex: "1:2.5 โ€” Risking $X to potentially gain $Y" - -5. **CONFIRMATION TRIGGER** - - Exact signal to wait for: e.g., "Bearish engulfing candle on rejection from VWAP zone" - - OBV: "Must be making lower highs + dropping below 30min average" - - RSI: "Should remain under 50 on rejection. Overbought โ‰ฅ70 = wait" - -6. **INDICATOR ANALYSIS** - - **RSI**: If RSI crosses above 70 while price is under resistance โ†’ *wait* - - **VWAP**: If price retakes VWAP with bullish momentum โ†’ *consider invalidation* - - **OBV**: If OBV starts climbing while price stays flat โ†’ *early exit or reconsider bias* - -Cross-reference all layouts to provide the most accurate analysis. If layouts show conflicting signals, explain which one takes priority and why. +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 **CRITICAL: You MUST analyze the actual chart images provided. Do not respond with generic advice.** -Return your answer as a JSON object with the following structure: +Provide your analysis in this exact JSON format (replace values with your analysis): + { - "summary": "Brief market summary combining all layouts", - "marketSentiment": "BULLISH" | "BEARISH" | "NEUTRAL", + "summary": "Objective description combining analysis from all charts", + "marketSentiment": "BULLISH|BEARISH|NEUTRAL", "keyLevels": { - "support": [number array], - "resistance": [number array] + "support": [list of visible support price levels as numbers], + "resistance": [list of visible resistance price levels as numbers] }, - "recommendation": "BUY" | "SELL" | "HOLD", - "confidence": number (0-100), - "reasoning": "Detailed reasoning with specific levels, indicators, and confirmation triggers from all layouts", + "recommendation": "BUY|SELL|HOLD", + "confidence": 75, + "reasoning": "Technical analysis reasoning based on indicators and price action from all layouts", "entry": { - "price": number, - "buffer": "string describing entry buffer", - "rationale": "string explaining entry logic" + "price": 150.50, + "buffer": "ยฑ0.25", + "rationale": "Technical reasoning for entry level" }, "stopLoss": { - "price": number, - "rationale": "string explaining stop loss placement" + "price": 148.00, + "rationale": "Technical reasoning for stop level" }, "takeProfits": { - "tp1": { "price": number, "description": "string" }, - "tp2": { "price": number, "description": "string" } + "tp1": { "price": 152.00, "description": "First target reasoning" }, + "tp2": { "price": 154.00, "description": "Second target reasoning" } }, - "riskToReward": "string like '1:2.5 - Risking $X to gain $Y'", - "confirmationTrigger": "string describing exact signal to wait for", + "riskToReward": "1:2", + "confirmationTrigger": "Technical signal to watch for", "indicatorAnalysis": { - "rsi": "string describing RSI behavior", - "vwap": "string describing VWAP behavior", - "obv": "string describing OBV behavior" + "rsi": "RSI level and interpretation", + "vwap": "VWAP relationship to price", + "obv": "Volume analysis if visible" } } -Be concise but thorough. Only return valid JSON.` +Return only the JSON object with your consolidated technical analysis.` const response = await openai.chat.completions.create({ model: "gpt-4o", // gpt-4o has better vision capabilities than gpt-4-vision-preview diff --git a/test-analysis-api.js b/test-analysis-api.js new file mode 100644 index 0000000..2914784 --- /dev/null +++ b/test-analysis-api.js @@ -0,0 +1,30 @@ +const fs = require('fs') +const path = require('path') + +async function testAnalysisAPI() { + try { + console.log('๐Ÿงช Testing AI analysis API with existing screenshots...') + + // Test the API by calling it directly (without requiring new screenshots) + const response = await fetch('http://localhost:3000/api/analyze', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + symbol: 'SOLUSD', + timeframe: '60', + useExisting: true // This will make it use existing screenshots + }) + }) + + const data = await response.json() + console.log('๐Ÿ“Š API Response:') + console.log(JSON.stringify(data, null, 2)) + + } catch (error) { + console.error('โŒ Error testing API:', error.message) + } +} + +testAnalysisAPI() diff --git a/test-direct-analysis.mjs b/test-direct-analysis.mjs new file mode 100644 index 0000000..afd5fb0 --- /dev/null +++ b/test-direct-analysis.mjs @@ -0,0 +1,37 @@ +import { aiAnalysisService } from '../lib/ai-analysis.js' +import fs from 'fs' + +async function testDirectAnalysis() { + try { + console.log('๐Ÿงช Testing AI analysis with existing screenshot...') + + const screenshotDir = '/app/screenshots' + const screenshots = fs.readdirSync(screenshotDir).filter(f => + f.includes('SOLUSD_60') && f.endsWith('.png') && !f.includes('debug') + ) + + console.log('๐Ÿ“ธ Available screenshots:', screenshots) + + if (screenshots.length > 0) { + const filename = screenshots[0] + console.log(`๐Ÿ” Analyzing: ${filename}`) + + const result = await aiAnalysisService.analyzeScreenshot(filename) + + if (result) { + console.log('โœ… Analysis successful!') + console.log('๐Ÿ“Š Analysis result:') + console.log(JSON.stringify(result, null, 2)) + } else { + console.log('โŒ Analysis failed - returned null') + } + } else { + console.log('โŒ No suitable screenshots found') + } + + } catch (error) { + console.error('โŒ Error in direct analysis test:', error) + } +} + +testDirectAnalysis()