Files
trading_bot_v3/lib/ai-analysis.ts
root 3361359119 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
2025-07-09 14:24:48 +02:00

209 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 515min 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 515min 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()