- Add proper handling when layouts are private/restricted even with login - Force re-authentication when restriction is detected without user menu - Fall back to base chart when layouts are inaccessible - Add detailed logging to AI analysis service to debug JSON parsing issues - Log full AI response content and extracted JSON for troubleshooting This should fix both the login restriction bypass and AI analysis failures.
215 lines
7.6 KiB
TypeScript
215 lines
7.6 KiB
TypeScript
import OpenAI from 'openai'
|
||
import fs from 'fs/promises'
|
||
import path from 'path'
|
||
|
||
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
|
||
}
|
||
|
||
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 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.
|
||
|
||
Analyze the attached TradingView chart screenshot and provide a detailed trading analysis.
|
||
|
||
### 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*
|
||
|
||
Return your answer as a JSON object with the following structure:
|
||
{
|
||
"summary": "Brief market summary",
|
||
"marketSentiment": "BULLISH" | "BEARISH" | "NEUTRAL",
|
||
"keyLevels": {
|
||
"support": [number array],
|
||
"resistance": [number array]
|
||
},
|
||
"recommendation": "BUY" | "SELL" | "HOLD",
|
||
"confidence": number (0-100),
|
||
"reasoning": "Detailed reasoning with specific levels, indicators, and confirmation triggers"
|
||
}
|
||
|
||
Be concise but thorough. Only return valid JSON.`
|
||
const response = await openai.chat.completions.create({
|
||
model: "gpt-4o", // Updated to current vision model
|
||
messages: [
|
||
{
|
||
role: "user",
|
||
content: [
|
||
{ type: "text", text: prompt },
|
||
{ type: "image_url", image_url: { url: `data:image/png;base64,${base64Image}` } }
|
||
]
|
||
}
|
||
],
|
||
max_tokens: 1024
|
||
})
|
||
const content = response.choices[0]?.message?.content
|
||
if (!content) return null
|
||
// Extract JSON from response
|
||
const match = content.match(/\{[\s\S]*\}/)
|
||
if (!match) return null
|
||
const json = match[0]
|
||
const result = JSON.parse(json)
|
||
// Optionally: validate result structure here
|
||
return result 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')
|
||
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 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.
|
||
|
||
Analyze the attached TradingView chart screenshots (multiple layouts of the same symbol) and provide a comprehensive trading analysis by combining insights from all charts.
|
||
|
||
### 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.
|
||
|
||
Return your answer as a JSON object with the following structure:
|
||
{
|
||
"summary": "Brief market summary combining all layouts",
|
||
"marketSentiment": "BULLISH" | "BEARISH" | "NEUTRAL",
|
||
"keyLevels": {
|
||
"support": [number array],
|
||
"resistance": [number array]
|
||
},
|
||
"recommendation": "BUY" | "SELL" | "HOLD",
|
||
"confidence": number (0-100),
|
||
"reasoning": "Detailed reasoning with specific levels, indicators, and confirmation triggers from all layouts"
|
||
}
|
||
|
||
Be concise but thorough. Only return valid JSON.`
|
||
|
||
const response = await openai.chat.completions.create({
|
||
model: "gpt-4o",
|
||
messages: [
|
||
{
|
||
role: "user",
|
||
content: [
|
||
{ type: "text", text: prompt },
|
||
...images
|
||
]
|
||
}
|
||
],
|
||
max_tokens: 1500,
|
||
temperature: 0.1
|
||
})
|
||
|
||
const content = response.choices[0]?.message?.content
|
||
if (!content) {
|
||
throw new Error('No content received from OpenAI')
|
||
}
|
||
|
||
console.log('AI response content:', content)
|
||
|
||
// Parse the 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])
|
||
|
||
// Validate the structure
|
||
if (!analysis.summary || !analysis.marketSentiment || !analysis.recommendation || !analysis.confidence) {
|
||
console.error('Invalid analysis structure:', analysis)
|
||
throw new Error('Invalid analysis structure')
|
||
}
|
||
|
||
return analysis
|
||
} catch (error) {
|
||
console.error('AI multi-analysis error:', error)
|
||
return null
|
||
}
|
||
}
|
||
}
|
||
|
||
export const aiAnalysisService = new AIAnalysisService()
|