fix: timeframe handling and progress tracking improvements

- Fix timeframe parameter handling in enhanced-screenshot API route
- Support both 'timeframe' (singular) and 'timeframes' (array) parameters
- Add proper sessionId propagation for real-time progress tracking
- Enhance MACD analysis prompt with detailed crossover definitions
- Add progress tracker service with Server-Sent Events support
- Fix Next.js build errors in chart components (module variable conflicts)
- Change dev environment port from 9000:3000 to 9001:3000
- Improve AI analysis layout detection logic
- Add comprehensive progress tracking through all service layers
This commit is contained in:
mindesbunister
2025-07-17 10:41:18 +02:00
parent 27df0304c6
commit ff4e9737fb
26 changed files with 1656 additions and 277 deletions

View File

@@ -1,5 +1,5 @@
"use client"
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import TradeModal from './TradeModal'
import ScreenshotGallery from './ScreenshotGallery'
@@ -38,6 +38,7 @@ interface ProgressStep {
}
interface AnalysisProgress {
sessionId: string
currentStep: number
totalSteps: number
steps: ProgressStep[]
@@ -60,6 +61,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
const [result, setResult] = useState<any>(null)
const [error, setError] = useState<string | null>(null)
const [progress, setProgress] = useState<AnalysisProgress | null>(null)
const [eventSource, setEventSource] = useState<EventSource | null>(null)
const [modalOpen, setModalOpen] = useState(false)
const [modalData, setModalData] = useState<any>(null)
const [enlargedScreenshot, setEnlargedScreenshot] = useState<string | null>(null)
@@ -77,6 +79,47 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
return String(value)
}
// Real-time progress tracking
const startProgressTracking = (sessionId: string) => {
// Close existing connection
if (eventSource) {
eventSource.close()
}
const es = new EventSource(`/api/progress/${sessionId}/stream`)
es.onmessage = (event) => {
try {
const progressData = JSON.parse(event.data)
if (progressData.type === 'complete') {
es.close()
setEventSource(null)
} else {
setProgress(progressData)
}
} catch (error) {
console.error('Error parsing progress data:', error)
}
}
es.onerror = (error) => {
console.error('EventSource error:', error)
es.close()
setEventSource(null)
}
setEventSource(es)
}
// Cleanup event source on unmount
React.useEffect(() => {
return () => {
if (eventSource) {
eventSource.close()
}
}
}, [eventSource])
const toggleLayout = (layout: string) => {
setSelectedLayouts(prev =>
prev.includes(layout)
@@ -93,104 +136,11 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
)
}
// Helper function to create initial progress steps
const createProgressSteps = (timeframes: string[], layouts: string[]): ProgressStep[] => {
const steps: ProgressStep[] = []
if (timeframes.length > 1) {
steps.push({
id: 'init',
title: 'Initializing Multi-Timeframe Analysis',
description: `Preparing to analyze ${timeframes.length} timeframes`,
status: 'pending'
})
} else {
steps.push({
id: 'init',
title: 'Initializing Analysis',
description: `Setting up screenshot service for ${layouts.join(', ')} layout(s)`,
status: 'pending'
})
}
// Helper function to create initial progress steps (no longer used - using real-time progress)
// const createProgressSteps = ...removed for real-time implementation
steps.push({
id: 'browser',
title: 'Starting Browser Sessions',
description: `Launching ${layouts.length} browser session(s)`,
status: 'pending'
})
steps.push({
id: 'auth',
title: 'TradingView Authentication',
description: 'Logging into TradingView accounts',
status: 'pending'
})
steps.push({
id: 'navigation',
title: 'Chart Navigation',
description: 'Navigating to chart layouts and timeframes',
status: 'pending'
})
steps.push({
id: 'loading',
title: 'Chart Data Loading',
description: 'Waiting for chart data and indicators to load',
status: 'pending'
})
steps.push({
id: 'capture',
title: 'Screenshot Capture',
description: 'Capturing high-quality chart screenshots',
status: 'pending'
})
steps.push({
id: 'analysis',
title: 'AI Analysis',
description: 'Analyzing screenshots with AI for trading insights',
status: 'pending'
})
return steps
}
// Helper function to update progress
const updateProgress = (stepId: string, status: ProgressStep['status'], details?: string) => {
setProgress(prev => {
if (!prev) return null
const updatedSteps = prev.steps.map(step => {
if (step.id === stepId) {
const updatedStep = {
...step,
status,
details: details || step.details
}
if (status === 'active' && !step.startTime) {
updatedStep.startTime = Date.now()
} else if ((status === 'completed' || status === 'error') && !step.endTime) {
updatedStep.endTime = Date.now()
}
return updatedStep
}
return step
})
const currentStepIndex = updatedSteps.findIndex(step => step.status === 'active')
return {
...prev,
steps: updatedSteps,
currentStep: currentStepIndex >= 0 ? currentStepIndex + 1 : prev.currentStep
}
})
}
// Helper function to update progress (no longer used - using real-time progress)
// const updateProgress = ...removed for real-time implementation
const performAnalysis = async (analysisSymbol = symbol, analysisTimeframes = selectedTimeframes) => {
if (loading || selectedLayouts.length === 0 || analysisTimeframes.length === 0) return
@@ -198,13 +148,51 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
setLoading(true)
setError(null)
setResult(null)
// Initialize progress tracking
const steps = createProgressSteps(analysisTimeframes, selectedLayouts)
// Set initial progress state to show animation immediately
setProgress({
currentStep: 0,
totalSteps: steps.length,
steps,
sessionId: 'initializing',
currentStep: 1,
totalSteps: 6,
steps: [
{
id: 'init',
title: 'Initializing Analysis',
description: 'Starting AI-powered trading analysis...',
status: 'active',
startTime: Date.now()
},
{
id: 'auth',
title: 'TradingView Authentication',
description: 'Logging into TradingView accounts',
status: 'pending'
},
{
id: 'navigation',
title: 'Chart Navigation',
description: 'Navigating to chart layouts',
status: 'pending'
},
{
id: 'loading',
title: 'Chart Data Loading',
description: 'Waiting for chart data and indicators',
status: 'pending'
},
{
id: 'capture',
title: 'Screenshot Capture',
description: 'Capturing high-quality screenshots',
status: 'pending'
},
{
id: 'analysis',
title: 'AI Analysis',
description: 'Analyzing screenshots with AI',
status: 'pending'
}
],
timeframeProgress: analysisTimeframes.length > 1 ? {
current: 0,
total: analysisTimeframes.length
@@ -212,14 +200,8 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
})
try {
updateProgress('init', 'active')
if (analysisTimeframes.length === 1) {
// Single timeframe analysis
await new Promise(resolve => setTimeout(resolve, 500)) // Brief pause for UI
updateProgress('init', 'completed')
updateProgress('browser', 'active', 'Starting browser session...')
// Single timeframe analysis with real-time progress
const response = await fetch('/api/enhanced-screenshot', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -231,35 +213,16 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
})
})
// Since we can't track internal API progress in real-time, we'll simulate logical progression
await new Promise(resolve => setTimeout(resolve, 1000))
updateProgress('browser', 'completed')
updateProgress('auth', 'active', 'Authenticating with TradingView...')
await new Promise(resolve => setTimeout(resolve, 2000))
updateProgress('auth', 'completed')
updateProgress('navigation', 'active', `Navigating to ${analysisSymbol} chart...`)
await new Promise(resolve => setTimeout(resolve, 2000))
updateProgress('navigation', 'completed')
updateProgress('loading', 'active', 'Loading chart data and indicators...')
await new Promise(resolve => setTimeout(resolve, 3000))
updateProgress('loading', 'completed')
updateProgress('capture', 'active', 'Capturing screenshots...')
const data = await response.json()
if (!response.ok) {
updateProgress('capture', 'error', data.error || 'Screenshot capture failed')
throw new Error(data.error || 'Analysis failed')
}
updateProgress('capture', 'completed', `Captured ${data.screenshots?.length || 0} screenshot(s)`)
updateProgress('analysis', 'active', 'Running AI analysis...')
await new Promise(resolve => setTimeout(resolve, 1000))
updateProgress('analysis', 'completed', 'Analysis complete!')
// Start real-time progress tracking if sessionId is provided
if (data.sessionId) {
startProgressTracking(data.sessionId)
}
setResult(data)
@@ -269,31 +232,14 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
}
} else {
// Multiple timeframe analysis
await new Promise(resolve => setTimeout(resolve, 500))
updateProgress('init', 'completed', `Starting analysis for ${analysisTimeframes.length} timeframes`)
const results = []
for (let i = 0; i < analysisTimeframes.length; i++) {
const tf = analysisTimeframes[i]
const timeframeLabel = timeframes.find(t => t.value === tf)?.label || tf
// Update timeframe progress
setProgress(prev => prev ? {
...prev,
timeframeProgress: {
...prev.timeframeProgress!,
current: i + 1,
currentTimeframe: timeframeLabel
}
} : null)
console.log(`🧪 Analyzing timeframe: ${timeframeLabel}`)
if (i === 0) {
updateProgress('browser', 'active', `Processing ${timeframeLabel} - Starting browser...`)
}
const response = await fetch('/api/enhanced-screenshot', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -305,21 +251,6 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
})
})
if (i === 0) {
updateProgress('browser', 'completed')
updateProgress('auth', 'active', `Processing ${timeframeLabel} - Authenticating...`)
await new Promise(resolve => setTimeout(resolve, 1000))
updateProgress('auth', 'completed')
}
updateProgress('navigation', 'active', `Processing ${timeframeLabel} - Navigating to chart...`)
await new Promise(resolve => setTimeout(resolve, 1000))
updateProgress('loading', 'active', `Processing ${timeframeLabel} - Loading chart data...`)
await new Promise(resolve => setTimeout(resolve, 1500))
updateProgress('capture', 'active', `Processing ${timeframeLabel} - Capturing screenshots...`)
const result = await response.json()
results.push({
timeframe: tf,
@@ -327,18 +258,26 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
success: response.ok,
result
})
// Start progress tracking for the first timeframe session
if (i === 0 && result.sessionId) {
startProgressTracking(result.sessionId)
}
updateProgress('analysis', 'active', `Processing ${timeframeLabel} - Running AI analysis...`)
// Update timeframe progress manually for multi-timeframe
setProgress(prev => prev ? {
...prev,
timeframeProgress: {
current: i + 1,
total: analysisTimeframes.length,
currentTimeframe: timeframeLabel
}
} : null)
// Small delay between requests
await new Promise(resolve => setTimeout(resolve, 2000))
await new Promise(resolve => setTimeout(resolve, 1000))
}
updateProgress('navigation', 'completed')
updateProgress('loading', 'completed')
updateProgress('capture', 'completed', `Captured screenshots for all ${analysisTimeframes.length} timeframes`)
updateProgress('analysis', 'completed', `Completed analysis for all timeframes!`)
const multiResult = {
type: 'multi_timeframe',
symbol: analysisSymbol,
@@ -493,7 +432,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
const isLeveraged = leverage > 1
// Route to appropriate API based on leverage
const apiEndpoint = isLeveraged ? '/api/trading/execute-perp' : '/api/trading/execute-dex'
const apiEndpoint = isLeveraged ? '/api/trading/execute-drift' : '/api/trading/execute-dex'
const tradingMode = isLeveraged ? 'PERP' : 'SPOT'
console.log(`🎯 Executing ${tradingMode} trade with ${leverage}x leverage via ${apiEndpoint}`)
@@ -522,20 +461,27 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
// Show detailed success message based on trading type
const leverage = parseFloat(tradeData.leverage) || 1
const isLeveraged = leverage > 1
const tradeType = isLeveraged ? 'Leveraged Position' : 'Spot Trade'
const tradeType = isLeveraged ? 'Leveraged Perpetual Position' : 'Spot Trade'
const platform = isLeveraged ? 'Drift Protocol' : 'Jupiter DEX'
let message = `${tradeType} executed successfully!\n\n`
message += `📊 Transaction ID: ${result.trade?.txId || result.txId}\n`
message += `💰 Symbol: ${tradeData.symbol || symbol}\n`
message += `📈 Size: ${tradeData.positionSize || tradeData.size} USDC\n`
if (isLeveraged) message += `⚡ Leverage: ${leverage}x\n`
message += `🏪 Platform: ${platform}\n`
if (isLeveraged) {
message += `⚡ Leverage: ${leverage}x (via increased position size)\n`
message += `<EFBFBD> Actual Trade Size: $${(parseFloat(tradeData.positionSize || tradeData.size) * leverage).toFixed(2)}\n`
}
message += `<EFBFBD>🏪 Platform: ${platform}\n`
if (tradeData.sl) message += `🛑 Stop Loss: $${tradeData.sl}\n`
if (tradeData.tp1) message += `🎯 Take Profit: $${tradeData.tp1}\n`
if (tradeData.sl) message += `🛑 Stop Loss: $${tradeData.sl} (Jupiter Trigger Order)\n`
if (tradeData.tp1) message += `🎯 Take Profit: $${tradeData.tp1} (Jupiter Trigger Order)\n`
if (result.trade?.monitoring || result.position) {
if (result.triggerOrders?.status === 'CREATED') {
message += `\n🔄 Trigger Orders: ACTIVE\n`
if (result.triggerOrders.stopLossOrderId) message += `🛑 SL Order: ${result.triggerOrders.stopLossOrderId.substring(0, 8)}...\n`
if (result.triggerOrders.takeProfitOrderId) message += `🎯 TP Order: ${result.triggerOrders.takeProfitOrderId.substring(0, 8)}...\n`
} else if (result.trade?.monitoring || result.position) {
message += `\n🔄 Position monitoring: ACTIVE`
}
@@ -550,6 +496,8 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
alert(`❌ Trade Failed: Insufficient Balance\n\nPlease ensure you have enough tokens in your wallet.\n\nError: ${errorMsg}`)
} else if (errorMsg.includes('Real Jupiter Perpetuals trading not yet implemented')) {
alert(`❌ Real Trading Not Available\n\nReal Jupiter Perpetuals trading is still in development. This trade will be simulated instead.\n\nTo use real spot trading, reduce the leverage to 1x.`)
} else if (errorMsg.includes('Trigger API error') || errorMsg.includes('trigger orders failed')) {
alert(`⚠️ Trade Executed, But Trigger Orders Failed\n\nYour main trade was successful, but stop loss/take profit orders could not be created.\n\nError: ${errorMsg}\n\nPlease monitor your position manually.`)
} else {
alert(`❌ Trade Failed\n\nError: ${errorMsg}`)
}