feat: Add persistent settings and multiple layouts support
- 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
This commit is contained in:
208
lib/ai-analysis.ts
Normal file
208
lib/ai-analysis.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user