import OpenAI from 'openai' import fs from 'fs/promises' import path from 'path' import { enhancedScreenshotService, ScreenshotConfig } from './enhanced-screenshot' import { TradingViewCredentials } from './tradingview-automation' import { progressTracker } from './progress-tracker' const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }) export interface AnalysisResult { summary: string marketSentiment: 'BULLISH' | 'BEARISH' | 'NEUTRAL' keyLevels: { support: number[] resistance: number[] } recommendation: 'BUY' | 'SELL' | 'HOLD' confidence: number // 0-100 reasoning: string // Enhanced trading analysis (optional) entry?: { price: number buffer?: string rationale: string } stopLoss?: { price: number rationale: string } takeProfits?: { tp1?: { price: number description: string rsiExpectation?: string obvExpectation?: string } tp2?: { price: number description: string rsiExpectation?: string obvExpectation?: string } } riskToReward?: string confirmationTrigger?: string indicatorAnalysis?: { rsi?: string vwap?: string obv?: string macd?: string crossLayoutConsensus?: string } layoutComparison?: { aiLayout?: string diyLayout?: string consensus?: string divergences?: string } timeframeRisk?: { assessment?: string positionSize?: string leverageRecommendation?: string } alternatives?: { tigherStopOption?: string scaledEntry?: string invalidationScenario?: string } } export class AIAnalysisService { async analyzeScreenshot(filenameOrPath: string): Promise { try { let imagePath: string // Check if it's already a full path or just a filename if (path.isAbsolute(filenameOrPath)) { // It's already a full path imagePath = filenameOrPath } else { // It's just a filename, construct the full path const screenshotsDir = path.join(process.cwd(), 'screenshots') imagePath = path.join(screenshotsDir, filenameOrPath) } // Read image file const imageBuffer = await fs.readFile(imagePath) const base64Image = imageBuffer.toString('base64') const prompt = `You are a professional trading assistant with expertise in technical analysis. You provide precise, actionable trading insights based on established technical analysis principles. **TECHNICAL ANALYSIS FUNDAMENTALS:** Before analyzing, understand these core indicator principles: **RSI (Relative Strength Index):** - Measures momentum on 0-100 scale - OVERBOUGHT: Above 70 (potential sell signal) - OVERSOLD: Below 30 (potential buy signal) - NEUTRAL: 30-70 range - ⚠️ CRITICAL: Read visual line position, not numerical value when they conflict **MACD (Moving Average Convergence Divergence):** - BULLISH CROSSOVER: MACD line crosses ABOVE signal line - BEARISH CROSSOVER: MACD line crosses BELOW signal line - HISTOGRAM: Green bars = bullish momentum, Red bars = bearish momentum - ZERO LINE: Above = bullish trend, Below = bearish trend **EMAs (Exponential Moving Averages):** - EMA 9 (Yellow): Short-term trend - EMA 20 (Orange): Medium-term trend - EMA 50 (Blue): Intermediate trend - EMA 200 (Red): Long-term trend - BULLISH STACK: 9 > 20 > 50 > 200 - BEARISH STACK: 9 < 20 < 50 < 200 **Stochastic RSI:** - OVERBOUGHT: Above 80 - OVERSOLD: Below 20 - BULLISH SIGNAL: %K crosses above %D in oversold territory - BEARISH SIGNAL: %K crosses below %D in overbought territory **VWAP (Volume Weighted Average Price):** - Above VWAP = Bullish sentiment - Below VWAP = Bearish sentiment - RECLAIM: Price moves back above VWAP (bullish) - REJECTION: Price fails at VWAP (bearish) **OBV (On-Balance Volume):** - Rising OBV = Volume supporting upward price movement - Falling OBV = Volume supporting downward price movement - DIVERGENCE: OBV direction differs from price (warning signal) **LAYOUT IDENTIFICATION:** **LAYOUT IDENTIFICATION:** **AI Layout (RSI + MACD + EMAs):** - TOP: RSI indicator (14-period momentum oscillator) - MIDDLE: EMAs (9,20,50,200) + ATR Bands + SVP - BOTTOM: MACD indicator with histogram - Focus: Momentum + Trend Analysis **DIY Layout (Stochastic RSI + VWAP + OBV):** - TOP: Stochastic RSI (more sensitive momentum) - MIDDLE: VWAP + Smart Money Concepts - BOTTOM: OBV (volume flow analysis) - Focus: Volume + Institutional Flow Analysis **TECHNICAL ANALYSIS PROCESS:** 1. **MOMENTUM ANALYSIS:** - AI Layout: Check RSI overbought/oversold conditions - DIY Layout: Check Stochastic RSI %K/%D crossovers - Look for momentum divergences with price 2. **TREND ANALYSIS:** - AI Layout: EMA stack order and price position - DIY Layout: VWAP position and smart money zones - Identify trend direction and strength 3. **VOLUME CONFIRMATION:** - AI Layout: Use MACD histogram for momentum confirmation - DIY Layout: Use OBV for volume flow confirmation - Volume should confirm price movements 4. **ENTRY/EXIT LEVELS:** - Use confluence of multiple indicators - Respect key technical levels (support/resistance) - Consider risk/reward ratios **TRADING SIGNALS:** **BULLISH SIGNALS:** - RSI oversold + MACD bullish crossover (AI Layout) - Stoch RSI oversold crossover + VWAP reclaim (DIY Layout) - Price above key EMAs in bullish stack - OBV rising with price (volume confirmation) **BEARISH SIGNALS:** - RSI overbought + MACD bearish crossover (AI Layout) - Stoch RSI overbought crossover + VWAP rejection (DIY Layout) - Price below key EMAs in bearish stack - OBV falling with price (volume confirmation) **TIMEFRAME RISK ASSESSMENT:** - **1m-15m**: High risk, 10x+ leverage, tight stops, scalping setups - **1H-4H**: Medium risk, 3-5x leverage, moderate stops, swing setups - **1D+**: Low risk, 1-2x leverage, wide stops, position setups **ANALYSIS REQUIREMENTS:** 1. **IDENTIFY LAYOUT TYPE**: AI Layout (RSI/MACD/EMAs) or DIY Layout (Stoch RSI/VWAP/OBV) 2. **MOMENTUM ASSESSMENT**: - Check primary momentum indicator (RSI or Stochastic RSI) - Look for overbought/oversold conditions - Identify momentum divergences 3. **TREND CONFIRMATION**: - EMA alignment and price position (AI Layout) - VWAP position and smart money zones (DIY Layout) - Determine trend direction and strength 4. **VOLUME ANALYSIS**: - MACD histogram momentum (AI Layout) - OBV volume flow confirmation (DIY Layout) - Volume should confirm price movements 5. **PRECISE TRADING LEVELS**: - **ENTRY**: Exact price with ±buffer and technical rationale - **STOP LOSS**: Exact level with clear reasoning - **TAKE PROFITS**: TP1 (structure) and TP2 (extension) with indicator expectations - **RISK/REWARD**: Calculate R:R ratio 6. **CONFIRMATION TRIGGERS**: Specific signals to wait for before entry **ANALYZE THE CHART AND PROVIDE:** - Current price action and trend direction - Key support and resistance levels visible on the chart - Technical indicator readings based on layout type - Chart patterns or formations - Market structure elements Provide your analysis in this exact JSON format: { "layoutDetected": "AI Layout|DIY Layout", "summary": "Comprehensive technical analysis with layout-specific indicator interpretation", "marketSentiment": "BULLISH|BEARISH|NEUTRAL", "keyLevels": { "support": [visible support price levels as numbers], "resistance": [visible resistance price levels as numbers] }, "recommendation": "BUY|SELL|HOLD", "confidence": 85, "reasoning": "Technical analysis reasoning based on established TA principles and indicator confluence", "momentumAnalysis": { "primary": "RSI OVERBOUGHT/OVERSOLD/NEUTRAL (AI Layout) or Stoch RSI status (DIY Layout)", "divergence": "Any momentum divergence with price action", "strength": "Momentum strength assessment" }, "trendAnalysis": { "direction": "BULLISH|BEARISH|NEUTRAL", "emaAlignment": "EMA stack order and price position (AI Layout)", "vwapPosition": "VWAP relationship to price (DIY Layout)", "strength": "Trend strength assessment" }, "volumeAnalysis": { "macdHistogram": "MACD momentum confirmation (AI Layout)", "obvFlow": "OBV volume flow analysis (DIY Layout)", "confirmation": "Volume confirming or diverging from price" }, "entry": { "price": 150.50, "buffer": "±0.25", "rationale": "Specific technical reasoning based on indicator confluence" }, "stopLoss": { "price": 148.00, "rationale": "Exact reasoning based on technical levels" }, "takeProfits": { "tp1": { "price": 152.00, "description": "First target based on structure", "indicatorExpectation": "Expected indicator behavior at TP1" }, "tp2": { "price": 154.00, "description": "Extended target for momentum continuation", "indicatorExpectation": "Expected indicator behavior at TP2" } }, "riskToReward": "1:2.5", "confirmationTrigger": "Specific signal to wait for before entry", "timeframeRisk": { "assessment": "Risk level based on timeframe", "positionSize": "Position sizing recommendation", "leverageRecommendation": "Leverage suggestion based on timeframe" }, "alternatives": { "tighterStop": "Alternative setup with tighter stop if needed", "scaledEntry": "Scaled entry approach if available", "invalidation": "What would invalidate this setup" } } Return only the JSON object with your technical analysis.` const response = await openai.chat.completions.create({ model: "gpt-4o-mini", // Cost-effective vision model messages: [ { role: "user", content: [ { type: "text", text: prompt }, { type: "image_url", image_url: { url: `data:image/png;base64,${base64Image}`, detail: "low" // Reduce token usage } } ] } ], 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) { console.error('No JSON found in response. Full content:', content) return null } const json = match[0] console.log('Raw JSON from AI:', json) let result try { result = JSON.parse(json) console.log('Parsed result:', result) } catch (parseError) { console.error('Failed to parse JSON:', parseError) console.error('Raw JSON that failed:', json) return null } // Sanitize the result to ensure no nested objects cause React issues const sanitizedResult = { summary: typeof result.summary === 'string' ? result.summary : String(result.summary || ''), marketSentiment: result.marketSentiment || 'NEUTRAL', keyLevels: { support: Array.isArray(result.keyLevels?.support) ? result.keyLevels.support : [], resistance: Array.isArray(result.keyLevels?.resistance) ? result.keyLevels.resistance : [] }, recommendation: result.recommendation || 'HOLD', confidence: typeof result.confidence === 'number' ? result.confidence : 0, reasoning: typeof result.reasoning === 'string' ? result.reasoning : String(result.reasoning || 'Basic technical analysis'), ...(result.entry && { entry: result.entry }), ...(result.stopLoss && { stopLoss: result.stopLoss }), ...(result.takeProfits && { takeProfits: result.takeProfits }), ...(result.riskToReward && { riskToReward: String(result.riskToReward) }), ...(result.confirmationTrigger && { confirmationTrigger: String(result.confirmationTrigger) }), ...(result.indicatorAnalysis && { indicatorAnalysis: result.indicatorAnalysis }) } // Optionally: validate result structure here return sanitizedResult as AnalysisResult } catch (e) { console.error('AI analysis error:', e) return null } } async analyzeMultipleScreenshots(filenamesOrPaths: string[]): Promise { try { // Read all image files and convert to base64 const images = await Promise.all( filenamesOrPaths.map(async (filenameOrPath) => { let imagePath: string // Check if it's already a full path or just a filename if (path.isAbsolute(filenameOrPath)) { // It's already a full path imagePath = filenameOrPath } else { // It's just a filename, construct the full path const screenshotsDir = path.join(process.cwd(), 'screenshots') imagePath = path.join(screenshotsDir, filenameOrPath) } 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 } } }) ) const layoutInfo = filenamesOrPaths.map(f => { const filename = path.basename(f) // Extract filename from path if (filename.includes('_ai_')) return 'AI Layout' if (f.includes('_diy_') || f.includes('_Diy module_')) return 'DIY Module Layout' return 'Unknown Layout' }).join(' and ') const prompt = `You are a professional trading assistant with expertise in technical analysis. You provide precise, actionable trading insights based on established technical analysis principles. I'm providing you with ${filenamesOrPaths.length} TradingView chart screenshots from different layouts: ${layoutInfo}. **TECHNICAL ANALYSIS FUNDAMENTALS:** **RSI (Relative Strength Index):** - OVERBOUGHT: Above 70 (potential sell signal) - OVERSOLD: Below 30 (potential buy signal) - NEUTRAL: 30-70 range - ⚠️ CRITICAL: Read visual line position, not numerical value when they conflict **MACD (Moving Average Convergence Divergence):** - BULLISH CROSSOVER: MACD line crosses ABOVE signal line - BEARISH CROSSOVER: MACD line crosses BELOW signal line - HISTOGRAM: Green bars = bullish momentum, Red bars = bearish momentum - ZERO LINE: Above = bullish trend, Below = bearish trend **EMAs (Exponential Moving Averages):** - EMA 9 (Yellow): Short-term trend - EMA 20 (Orange): Medium-term trend - EMA 50 (Blue): Intermediate trend - EMA 200 (Red): Long-term trend - BULLISH STACK: 9 > 20 > 50 > 200 - BEARISH STACK: 9 < 20 < 50 < 200 **Stochastic RSI:** - OVERBOUGHT: Above 80 - OVERSOLD: Below 20 - BULLISH SIGNAL: %K crosses above %D in oversold territory - BEARISH SIGNAL: %K crosses below %D in overbought territory **VWAP (Volume Weighted Average Price):** - Above VWAP = Bullish sentiment - Below VWAP = Bearish sentiment - RECLAIM: Price moves back above VWAP (bullish) - REJECTION: Price fails at VWAP (bearish) **OBV (On-Balance Volume):** - Rising OBV = Volume supporting upward price movement - Falling OBV = Volume supporting downward price movement - DIVERGENCE: OBV direction differs from price (warning signal) **MULTI-LAYOUT ANALYSIS:** **MULTI-LAYOUT ANALYSIS:** **AI Layout (RSI + MACD + EMAs):** - Focus: Momentum + Trend Analysis - Primary indicators: RSI, MACD, EMAs - Use for: Trend direction, momentum signals, entry/exit timing **DIY Layout (Stochastic RSI + VWAP + OBV):** - Focus: Volume + Institutional Flow Analysis - Primary indicators: Stochastic RSI, VWAP, OBV - Use for: Volume confirmation, institutional sentiment, fair value **ANALYSIS PROCESS:** 1. **Identify Layout Types**: Determine which layouts are provided 2. **Momentum Assessment**: Check primary momentum indicators 3. **Trend Confirmation**: Analyze trend direction and strength 4. **Volume Analysis**: Confirm with volume indicators 5. **Cross-Layout Consensus**: Compare insights for confirmation 6. **Risk Assessment**: Adjust for timeframe and volatility **TIMEFRAME RISK ASSESSMENT:** - **1m-15m**: High risk, 10x+ leverage, tight stops, scalping setups - **1H-4H**: Medium risk, 3-5x leverage, moderate stops, swing setups - **1D+**: Low risk, 1-2x leverage, wide stops, position setups **PROVIDE PRECISE TRADING LEVELS:** - **ENTRY**: Exact price with ±buffer and technical rationale - **STOP LOSS**: Exact level with clear reasoning - **TAKE PROFITS**: TP1 (structure) and TP2 (extension) with indicator expectations - **RISK/REWARD**: Calculate R:R ratio - **CONFIRMATION TRIGGERS**: Specific signals to wait for **ANALYZE THE CHARTS AND PROVIDE:** - Current price action and trend direction - Key support and resistance levels - Technical indicator readings based on layout types - Cross-layout consensus and divergences - Market structure elements **Response Format** (return only valid JSON): { "summary": "Comprehensive multi-layout analysis with cross-layout consensus and TA fundamentals", "marketSentiment": "BULLISH|BEARISH|NEUTRAL", "keyLevels": { "support": [array of support levels from all charts], "resistance": [array of resistance levels from all charts] }, "recommendation": "BUY|SELL|HOLD", "confidence": 85, "reasoning": "Multi-layout technical analysis with cross-layout confirmation", "layoutsAnalyzed": ["AI Layout", "DIY Layout"], "momentumAnalysis": { "aiLayout": "RSI analysis from AI layout", "diyLayout": "Stochastic RSI analysis from DIY layout", "consensus": "Momentum consensus between layouts", "divergence": "Any momentum divergences detected" }, "trendAnalysis": { "aiLayout": "EMA alignment and trend from AI layout", "diyLayout": "VWAP position and smart money from DIY layout", "consensus": "Trend consensus between layouts", "direction": "BULLISH|BEARISH|NEUTRAL" }, "volumeAnalysis": { "aiLayout": "MACD histogram momentum from AI layout", "diyLayout": "OBV volume flow from DIY layout", "consensus": "Volume consensus between layouts", "confirmation": "Volume confirming or diverging from price" }, "entry": { "price": 150.50, "buffer": "±0.25", "rationale": "Cross-layout confluence supporting entry level" }, "stopLoss": { "price": 148.00, "rationale": "Technical level confirmed by multiple layouts" }, "takeProfits": { "tp1": { "price": 152.00, "description": "First target with multi-layout support", "indicatorExpectation": "Expected behavior across both layouts" }, "tp2": { "price": 154.00, "description": "Extended target for momentum continuation", "indicatorExpectation": "Extended momentum expectations" } }, "riskToReward": "1:2.5", "confirmationTrigger": "Specific cross-layout signal to wait for", "layoutComparison": { "aiLayoutEdge": "Specific advantage of AI layout analysis", "diyLayoutEdge": "Specific advantage of DIY layout analysis", "consensus": "Areas where both layouts strongly agree", "divergences": "Areas where layouts disagree and resolution" }, "timeframeRisk": { "assessment": "Risk level based on timeframe", "positionSize": "Position sizing recommendation", "leverageRecommendation": "Leverage suggestion for timeframe" }, "alternatives": { "tighterStop": "Alternative with tighter stop if needed", "scaledEntry": "Scaled entry approach if available", "invalidation": "What would invalidate this setup" } } 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 ${filenamesOrPaths.length} screenshots to OpenAI for multi-layout analysis...`) const response = await openai.chat.completions.create({ 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 response from OpenAI') } console.log('🔍 Raw OpenAI response:', content.substring(0, 200) + '...') // Parse JSON response const jsonMatch = content.match(/\{[\s\S]*\}/) if (!jsonMatch) { throw new Error('No JSON found in response') } const analysis = JSON.parse(jsonMatch[0]) console.log('✅ Multi-layout analysis parsed successfully') return analysis as AnalysisResult } catch (error: any) { console.error('❌ Multi-screenshot AI analysis failed:', error.message) console.error('Full error:', error) return null } } async captureAndAnalyze( symbol: string, timeframe: string, credentials: TradingViewCredentials ): Promise { try { console.log(`Starting automated capture and analysis for ${symbol} ${timeframe}`) // Capture screenshot using automation const screenshot = await enhancedScreenshotService.captureQuick(symbol, timeframe, credentials) if (!screenshot) { throw new Error('Failed to capture screenshot') } console.log(`Screenshot captured: ${screenshot}`) // Analyze the captured screenshot const analysis = await this.analyzeScreenshot(screenshot) if (!analysis) { throw new Error('Failed to analyze screenshot') } console.log(`Analysis completed for ${symbol} ${timeframe}`) return analysis } catch (error) { console.error('Automated capture and analysis failed:', error) return null } } async captureAndAnalyzeMultiple( symbols: string[], timeframes: string[], credentials: TradingViewCredentials ): Promise> { const results: Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }> = [] for (const symbol of symbols) { for (const timeframe of timeframes) { try { console.log(`Processing ${symbol} ${timeframe}...`) const analysis = await this.captureAndAnalyze(symbol, timeframe, credentials) results.push({ symbol, timeframe, analysis }) // Small delay between captures to avoid overwhelming the system await new Promise(resolve => setTimeout(resolve, 2000)) } catch (error) { console.error(`Failed to process ${symbol} ${timeframe}:`, error) results.push({ symbol, timeframe, analysis: null }) } } } return results } async captureAndAnalyzeWithConfig(config: ScreenshotConfig): Promise<{ screenshots: string[] analysis: AnalysisResult | null }> { const { sessionId } = config try { console.log(`Starting automated capture with config for ${config.symbol} ${config.timeframe}`) // Capture screenshots using enhanced service (this will handle its own progress) const screenshots = await enhancedScreenshotService.captureWithLogin(config) if (screenshots.length === 0) { throw new Error('No screenshots captured') } console.log(`${screenshots.length} screenshot(s) captured`) // Add AI analysis step to progress if sessionId exists if (sessionId) { progressTracker.updateStep(sessionId, 'analysis', 'active', 'Running AI analysis on screenshots...') } let analysis: AnalysisResult | null = null if (screenshots.length === 1) { // Single screenshot analysis analysis = await this.analyzeScreenshot(screenshots[0]) } else { // Multiple screenshots analysis analysis = await this.analyzeMultipleScreenshots(screenshots) } if (!analysis) { if (sessionId) { progressTracker.updateStep(sessionId, 'analysis', 'error', 'AI analysis failed to generate results') } throw new Error('Failed to analyze screenshots') } console.log(`Analysis completed for ${config.symbol} ${config.timeframe}`) if (sessionId) { progressTracker.updateStep(sessionId, 'analysis', 'completed', 'AI analysis completed successfully!') // Mark session as complete setTimeout(() => progressTracker.deleteSession(sessionId), 1000) } // Trigger post-analysis cleanup in development mode if (process.env.NODE_ENV === 'development') { try { // Dynamic import to avoid circular dependencies const aggressiveCleanupModule = await import('./aggressive-cleanup') const aggressiveCleanup = aggressiveCleanupModule.default // Run cleanup in background, don't block the response aggressiveCleanup.runPostAnalysisCleanup().catch(console.error) } catch (cleanupError) { console.error('Error triggering post-analysis cleanup:', cleanupError) } } return { screenshots, analysis } } catch (error) { console.error('Automated capture and analysis with config failed:', error) if (sessionId) { // Find the active step and mark it as error const progress = progressTracker.getProgress(sessionId) if (progress) { const activeStep = progress.steps.find(step => step.status === 'active') if (activeStep) { progressTracker.updateStep(sessionId, activeStep.id, 'error', error instanceof Error ? error.message : 'Unknown error') } } // Clean up session setTimeout(() => progressTracker.deleteSession(sessionId), 5000) } return { screenshots: [], analysis: null } } } } export const aiAnalysisService = new AIAnalysisService()