- Updated AI prompts to behave like professional proprietary desk trader - Enhanced take profit display with RSI/OBV expectations and detailed descriptions - Added timeframe risk assessment with leverage recommendations - Implemented comprehensive indicator analysis (RSI, VWAP, OBV, MACD) - Added alternatives section with tighter stops and scaled entry options - Enhanced test script output for better trade setup visualization - Improved confirmation triggers with specific technical signals - Added cross-layout consensus analysis for multi-screenshot comparison - Cost-effective analysis using gpt-4o mini (~/usr/bin/bash.006 per analysis)
538 lines
19 KiB
TypeScript
538 lines
19 KiB
TypeScript
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<AnalysisResult | null> {
|
|
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<AnalysisResult | null> {
|
|
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<AnalysisResult | null> {
|
|
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<Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }>> {
|
|
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()
|