- Add settings manager to persist symbol, timeframe, and layouts - Support multiple layouts for comprehensive chart analysis - Remove debug screenshots for cleaner logs - Update AI analysis with professional trading prompt - Add multi-screenshot analysis for better trading insights - Update analyze API to use saved settings and multiple layouts
209 lines
7.4 KiB
TypeScript
209 lines
7.4 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')
|
||
}
|
||
|
||
// Parse the JSON response
|
||
const jsonMatch = content.match(/\{[\s\S]*\}/)
|
||
if (!jsonMatch) {
|
||
throw new Error('No JSON found in response')
|
||
}
|
||
|
||
const analysis = JSON.parse(jsonMatch[0])
|
||
|
||
// Validate the structure
|
||
if (!analysis.summary || !analysis.marketSentiment || !analysis.recommendation || !analysis.confidence) {
|
||
throw new Error('Invalid analysis structure')
|
||
}
|
||
|
||
return analysis
|
||
} catch (error) {
|
||
console.error('AI multi-analysis error:', error)
|
||
return null
|
||
}
|
||
}
|
||
}
|
||
|
||
export const aiAnalysisService = new AIAnalysisService()
|