- Updated system prompt to match main analysis style: precision of proprietary desk trader - Reduced max tokens from 800 to 200 for main responses - Reduced screenshot analysis tokens from 1000 to 150 - Made welcome messages more compact and focused - Shortened quick action buttons (Exit now?, Move stop loss, etc.) - Condensed status report to essential information only - Eliminated verbose explanations, focus on exact price levels and immediate actions - Changed temperature from 0.3 to 0.1 for more consistent responses Addresses user feedback that responses were 'way too vague and too much talk'.
178 lines
5.3 KiB
JavaScript
178 lines
5.3 KiB
JavaScript
import { NextResponse } from 'next/server'
|
|
import OpenAI from 'openai'
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
|
|
const openai = new OpenAI({
|
|
apiKey: process.env.OPENAI_API_KEY
|
|
})
|
|
|
|
// Helper function to convert image file to base64
|
|
function imageToBase64(imagePath) {
|
|
try {
|
|
const fullPath = path.join(process.cwd(), 'screenshots', imagePath)
|
|
if (fs.existsSync(fullPath)) {
|
|
const imageBuffer = fs.readFileSync(fullPath)
|
|
return imageBuffer.toString('base64')
|
|
}
|
|
return null
|
|
} catch (error) {
|
|
console.error('Error converting image to base64:', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
export async function POST(request) {
|
|
try {
|
|
const { message, position, screenshots, chatHistory } = await request.json()
|
|
|
|
if (!message || !position) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: 'Message and position are required'
|
|
}, { status: 400 })
|
|
}
|
|
|
|
// Build context about the current position
|
|
const positionContext = `
|
|
CURRENT POSITION DETAILS:
|
|
- Symbol: ${position.symbol}
|
|
- Side: ${position.side}
|
|
- Entry Price: $${position.entryPrice}
|
|
- Current Price: $${position.currentPrice || 'Unknown'}
|
|
- Position Size: ${position.size}
|
|
- Current P&L: ${position.pnl > 0 ? '+' : ''}$${position.pnl?.toFixed(2) || 'Unknown'}
|
|
- Stop Loss: ${position.stopLoss ? `$${position.stopLoss}` : 'Not set'}
|
|
- Take Profit: ${position.takeProfit ? `$${position.takeProfit}` : 'Not set'}
|
|
- Entry Time: ${position.entryTime}
|
|
- Entry Analysis: ${position.entryAnalysis || 'Not available'}
|
|
`
|
|
|
|
// Build chat history context
|
|
const chatContext = chatHistory?.length > 0
|
|
? `\n\nRECENT CONVERSATION:\n${chatHistory.map((msg) =>
|
|
`${msg.type === 'user' ? 'TRADER' : 'ASSISTANT'}: ${msg.content}`
|
|
).join('\n')}`
|
|
: ''
|
|
|
|
// Analyze screenshots if provided
|
|
let screenshotAnalysis = ''
|
|
if (screenshots && screenshots.length > 0) {
|
|
console.log('📸 Processing screenshots for analysis:', screenshots.length)
|
|
|
|
const screenshotMessages = []
|
|
|
|
for (const screenshot of screenshots) {
|
|
// Extract filename from screenshot path/URL
|
|
const filename = screenshot.split('/').pop() || screenshot
|
|
console.log('🔍 Processing screenshot:', filename)
|
|
|
|
// Convert to base64
|
|
const base64Image = imageToBase64(filename)
|
|
if (base64Image) {
|
|
screenshotMessages.push({
|
|
type: "image_url",
|
|
image_url: {
|
|
url: `data:image/png;base64,${base64Image}`,
|
|
detail: "high"
|
|
}
|
|
})
|
|
} else {
|
|
console.warn('⚠️ Failed to convert screenshot to base64:', filename)
|
|
}
|
|
}
|
|
|
|
if (screenshotMessages.length > 0) {
|
|
console.log('🤖 Sending screenshots to OpenAI for analysis...')
|
|
const analysisResponse = await openai.chat.completions.create({
|
|
model: "gpt-4o-mini",
|
|
messages: [
|
|
{
|
|
role: "system",
|
|
content: `You are a professional trading analyst. Analyze this chart for an active ${position.side} position at $${position.entryPrice}.
|
|
|
|
Current P&L: ${position.pnl > 0 ? '+' : ''}$${position.pnl?.toFixed(2)}
|
|
|
|
PROVIDE CONCISE ANALYSIS (Max 100 words):
|
|
• Current price action vs entry
|
|
• Key levels to watch
|
|
• Risk assessment
|
|
• Immediate action needed
|
|
|
|
Be direct. Give exact price levels only.`
|
|
},
|
|
{
|
|
role: "user",
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Analyze these current chart screenshots for my ${position.side} position in ${position.symbol}. What should I do now?`
|
|
},
|
|
...screenshotMessages
|
|
]
|
|
}
|
|
],
|
|
max_tokens: 150,
|
|
temperature: 0.1
|
|
})
|
|
|
|
screenshotAnalysis = analysisResponse.choices[0]?.message?.content || ''
|
|
console.log('✅ Screenshot analysis completed')
|
|
}
|
|
}
|
|
|
|
// Generate conversational response
|
|
const systemPrompt = `You are a professional trading coach with the precision of a top proprietary desk trader. No vagueness, no fluff.
|
|
|
|
CURRENT POSITION:
|
|
${positionContext}
|
|
|
|
${screenshotAnalysis ? `LATEST CHART ANALYSIS:\n${screenshotAnalysis}\n` : ''}
|
|
|
|
RESPONSE STYLE:
|
|
- Be direct and actionable
|
|
- Give EXACT price levels only
|
|
- Use bullet points for clarity
|
|
- Maximum 150 words total
|
|
- Focus on immediate action needed
|
|
|
|
TRADER QUESTION: "${message}"
|
|
|
|
Provide concise, specific guidance.`
|
|
|
|
const response = await openai.chat.completions.create({
|
|
model: "gpt-4o-mini",
|
|
messages: [
|
|
{
|
|
role: "system",
|
|
content: systemPrompt
|
|
},
|
|
{
|
|
role: "user",
|
|
content: message
|
|
}
|
|
],
|
|
max_tokens: 200,
|
|
temperature: 0.1
|
|
})
|
|
|
|
const assistantResponse = response.choices[0]?.message?.content
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
response: assistantResponse,
|
|
analysis: screenshotAnalysis ? {
|
|
timestamp: new Date().toISOString(),
|
|
content: screenshotAnalysis
|
|
} : null
|
|
})
|
|
|
|
} catch (error) {
|
|
console.error('Trade follow-up error:', error)
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: 'Failed to process trade follow-up request'
|
|
}, { status: 500 })
|
|
}
|
|
}
|