import OpenAI from 'openai' import fs from 'fs/promises' import path from 'path' import { enhancedScreenshotService, ScreenshotConfig } from './enhanced-screenshot' import { TradingViewCredentials } from './tradingview-automation' 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(filename: string): Promise { try { const screenshotsDir = path.join(process.cwd(), 'screenshots') const imagePath = path.join(screenshotsDir, filename) // Read image file const imageBuffer = await fs.readFile(imagePath) const base64Image = imageBuffer.toString('base64') const prompt = `You are now a professional trading assistant. You behave with the precision and decisiveness of a top proprietary desk trader. No vagueness, no fluff. **TRADING ANALYSIS REQUIREMENTS:** 1. **TIMEFRAME RISK ASSESSMENT**: Based on the timeframe shown in the screenshot, adjust risk accordingly: - Lower timeframes (1m-15m): Higher risk, use at least 10x leverage for scalps, smaller position sizes - Higher timeframes (4H+): Lower risk, larger position sizes, swing trades - 5-minute scalps require at least 10x leverage 2. **BE 100% SPECIFIC** - Provide exact levels with clear rationale: **ENTRY**: Exact price level (with ± buffer if needed) - Rationale: e.g., "Rejection from 15 EMA + VWAP confluence near intraday supply" **STOP-LOSS**: Exact level (not arbitrary) - Explain WHY it's there: "Above VWAP + failed breakout zone" **TAKE PROFITS**: - TP1: Immediate structure (previous low/high, key level) - TP2: Extended target if momentum continues - Mention expected RSI/OBV behavior at each TP zone 3. **RISK-TO-REWARD**: Show R:R ratio with dollar amounts if possible 4. **CONFIRMATION TRIGGERS**: Exact signals to wait for: - Specific candle patterns, indicator crosses, volume confirmations - RSI behavior: "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" 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 technical analysis with timeframe risk assessment and specific trading setup", "marketSentiment": "BULLISH|BEARISH|NEUTRAL", "keyLevels": { "support": [list of visible support price levels as numbers], "resistance": [list of visible resistance price levels as numbers] }, "recommendation": "BUY|SELL|HOLD", "confidence": 75, "reasoning": "Specific technical analysis reasoning with exact rationale for each level", "entry": { "price": 150.50, "buffer": "±0.25", "rationale": "Specific technical reasoning: rejection from 15 EMA + VWAP confluence near intraday supply" }, "stopLoss": { "price": 148.00, "rationale": "Exact reasoning: Above VWAP + failed breakout zone" }, "takeProfits": { "tp1": { "price": 152.00, "description": "Immediate structure target with indicator expectations", "rsiExpectation": "RSI should reach 60-65 zone", "obvExpectation": "OBV confirming momentum" }, "tp2": { "price": 154.00, "description": "Extended target if momentum continues", "rsiExpectation": "RSI approaching 70+ overbought", "obvExpectation": "OBV making new highs with price" } }, "riskToReward": "1:2", "confirmationTrigger": "Specific signal: Bearish engulfing candle on rejection from VWAP zone with RSI under 50", "indicatorAnalysis": { "rsi": "Specific RSI level and precise interpretation with action triggers", "vwap": "VWAP relationship to price with exact invalidation levels", "obv": "Volume analysis with specific behavioral expectations", "macd": "MACD signal line crosses and momentum analysis" }, "timeframeRisk": { "assessment": "Risk level based on detected timeframe", "positionSize": "Suggested position sizing based on timeframe", "leverageRecommendation": "Specific leverage suggestion for the timeframe" }, "alternatives": { "tigherStopOption": "Alternative setup with tighter stop if original SL is too far", "scaledEntry": "Scaled entry approach if better levels become available", "invalidationScenario": "What price action would invalidate this setup completely" } } 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(filenames: string[]): Promise { try { const screenshotsDir = path.join(process.cwd(), 'screenshots') // 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 } } }) ) 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 ') const prompt = `You are now a professional trading assistant. You behave with the precision and decisiveness of a top proprietary desk trader. No vagueness, no fluff. I'm providing you with ${filenames.length} TradingView chart screenshots from different layouts: ${layoutInfo}. **TRADING ANALYSIS REQUIREMENTS:** 1. **TIMEFRAME RISK ASSESSMENT**: Based on the timeframe shown in the screenshots, adjust risk accordingly: - Lower timeframes (1m-15m): Higher risk, use at least 10x leverage for scalps, smaller position sizes - Higher timeframes (4H+): Lower risk, larger position sizes, swing trades - 5-minute scalps require at least 10x leverage 2. **BE 100% SPECIFIC** - Provide exact levels with clear rationale: **ENTRY**: Exact price level (with ± buffer if needed) - Rationale: e.g., "Rejection from 15 EMA + VWAP confluence near intraday supply" **STOP-LOSS**: Exact level (not arbitrary) - Explain WHY it's there: "Above VWAP + failed breakout zone" **TAKE PROFITS**: - TP1: Immediate structure (previous low/high, key level) - TP2: Extended target if momentum continues - Mention expected RSI/OBV behavior at each TP zone 3. **RISK-TO-REWARD**: Show R:R ratio with dollar amounts if possible 4. **CONFIRMATION TRIGGERS**: Exact signals to wait for: - Specific candle patterns, indicator crosses, volume confirmations - RSI behavior: "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" 5. **CROSS-LAYOUT ANALYSIS**: - Compare insights from different chart layouts for confirmations/contradictions - Use multiple perspectives to increase confidence - Note which layout provides clearest signals 6. **ALTERNATIVES**: If SL is far, offer tighter alternatives or scaled entries **Response Format** (return only valid JSON): { "summary": "Comprehensive multi-layout analysis with timeframe risk assessment and cross-layout insights", "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 specific rationale for each level and timeframe-adjusted risk assessment", "entry": { "price": 150.50, "buffer": "±0.25", "rationale": "Specific technical reasoning: rejection from 15 EMA + VWAP confluence near intraday supply" }, "stopLoss": { "price": 148.00, "rationale": "Exact reasoning: Above VWAP + failed breakout zone" }, "takeProfits": { "tp1": { "price": 152.00, "description": "Immediate structure target with RSI/OBV expectations", "rsiExpectation": "RSI should reach 60-65 zone", "obvExpectation": "OBV confirming momentum" }, "tp2": { "price": 154.00, "description": "Extended target if momentum continues", "rsiExpectation": "RSI approaching 70+ overbought", "obvExpectation": "OBV making new highs with price" } }, "riskToReward": "1:2.5", "confirmationTrigger": "Specific signal: Bearish engulfing candle on rejection from VWAP zone with RSI under 50", "indicatorAnalysis": { "rsi": "Specific RSI level and precise interpretation with action triggers", "vwap": "VWAP relationship to price with exact invalidation levels", "obv": "Volume analysis with specific behavioral expectations", "macd": "MACD signal line crosses and momentum analysis", "crossLayoutConsensus": "Detailed comparison of how different layouts confirm or contradict signals" }, "layoutComparison": { "aiLayout": "Specific insights and edge from AI layout analysis", "diyLayout": "Specific insights and edge from DIY module layout analysis", "consensus": "Exact areas where both layouts strongly agree with confidence boost", "divergences": "Specific contradictions between layouts and how to resolve them" }, "timeframeRisk": { "assessment": "Risk level based on detected timeframe", "positionSize": "Suggested position sizing based on timeframe", "leverageRecommendation": "Specific leverage suggestion for the timeframe" }, "alternatives": { "tigherStopOption": "Alternative setup with tighter stop if original SL is too far", "scaledEntry": "Scaled entry approach if better levels become available", "invalidationScenario": "What price action would invalidate this setup completely" } } 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-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 }> { try { console.log(`Starting automated capture with config for ${config.symbol} ${config.timeframe}`) // Capture screenshots using enhanced service const screenshots = await enhancedScreenshotService.captureWithLogin(config) if (screenshots.length === 0) { throw new Error('No screenshots captured') } console.log(`${screenshots.length} screenshot(s) captured`) 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) { throw new Error('Failed to analyze screenshots') } console.log(`Analysis completed for ${config.symbol} ${config.timeframe}`) return { screenshots, analysis } } catch (error) { console.error('Automated capture and analysis with config failed:', error) return { screenshots: [], analysis: null } } } } export const aiAnalysisService = new AIAnalysisService()