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,19 +1,70 @@
import { NextResponse } from 'next/server'
import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot-simple'
import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot'
import { aiAnalysisService } from '../../../lib/ai-analysis'
import { progressTracker } from '../../../lib/progress-tracker'
export async function POST(request) {
try {
const body = await request.json()
const { symbol, layouts, timeframes, selectedLayouts, analyze = true } = body
const { symbol, layouts, timeframe, timeframes, selectedLayouts, analyze = true } = body
console.log('📊 Enhanced screenshot request:', { symbol, layouts, timeframes, selectedLayouts })
console.log('📊 Enhanced screenshot request:', { symbol, layouts, timeframe, timeframes, selectedLayouts })
// Generate unique session ID for progress tracking
const sessionId = `analysis_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
console.log('🔍 Created session ID:', sessionId)
// Create progress tracking session with initial steps
const initialSteps = [
{
id: 'init',
title: 'Initializing Analysis',
description: 'Starting AI-powered trading analysis...',
status: 'pending'
},
{
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'
}
]
// Create the progress session
console.log('🔍 Creating progress session with steps:', initialSteps.length)
progressTracker.createSession(sessionId, initialSteps)
console.log('🔍 Progress session created successfully')
// Prepare configuration for screenshot service
const config = {
symbol: symbol || 'BTCUSD',
timeframe: timeframes?.[0] || '60', // Use first timeframe for now
timeframe: timeframe || timeframes?.[0] || '60', // Use single timeframe, fallback to first of array, then default
layouts: layouts || selectedLayouts || ['ai'],
sessionId, // Pass session ID for progress tracking
credentials: {
email: process.env.TRADINGVIEW_EMAIL,
password: process.env.TRADINGVIEW_PASSWORD
@@ -22,35 +73,32 @@ export async function POST(request) {
console.log('🔧 Using config:', config)
// Capture screenshots using the working service
const screenshots = await enhancedScreenshotService.captureWithLogin(config)
console.log('📸 Screenshots captured:', screenshots)
let screenshots = []
let analysis = null
// Perform AI analysis if requested and screenshots were captured
if (analyze && screenshots.length > 0) {
// Perform AI analysis if requested
if (analyze) {
try {
console.log('🤖 Starting AI analysis...')
// Extract just the filenames from full paths
const filenames = screenshots.map(path => path.split('/').pop())
if (filenames.length === 1) {
analysis = await aiAnalysisService.analyzeScreenshot(filenames[0])
} else {
analysis = await aiAnalysisService.analyzeMultipleScreenshots(filenames)
}
console.log('✅ AI analysis completed')
console.log('🤖 Starting automated capture and analysis...')
const result = await aiAnalysisService.captureAndAnalyzeWithConfig(config, sessionId)
screenshots = result.screenshots
analysis = result.analysis
console.log('✅ Automated capture and analysis completed')
} catch (analysisError) {
console.error('❌ AI analysis failed:', analysisError)
// Continue without analysis rather than failing the whole request
console.error('❌ Automated capture and analysis failed:', analysisError)
// Fall back to screenshot only
screenshots = await enhancedScreenshotService.captureWithLogin(config, sessionId)
}
} else {
// Capture screenshots only
screenshots = await enhancedScreenshotService.captureWithLogin(config, sessionId)
}
console.log('📸 Final screenshots:', screenshots)
const result = {
success: true,
sessionId, // Return session ID for progress tracking
timestamp: Date.now(),
symbol: config.symbol,
layouts: config.layouts,

View File

@@ -0,0 +1,58 @@
import { NextRequest, NextResponse } from 'next/server'
import { progressTracker } from '../../../../../lib/progress-tracker'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ sessionId: string }> }
) {
const { sessionId } = await params
// Create a readable stream for Server-Sent Events
const encoder = new TextEncoder()
const stream = new ReadableStream({
start(controller) {
// Send initial progress if session exists
const initialProgress = progressTracker.getProgress(sessionId)
if (initialProgress) {
const data = `data: ${JSON.stringify(initialProgress)}\n\n`
controller.enqueue(encoder.encode(data))
}
// Listen for progress updates
const progressHandler = (progress: any) => {
const data = `data: ${JSON.stringify(progress)}\n\n`
controller.enqueue(encoder.encode(data))
}
// Listen for completion
const completeHandler = () => {
const data = `data: ${JSON.stringify({ type: 'complete' })}\n\n`
controller.enqueue(encoder.encode(data))
controller.close()
}
// Subscribe to events
progressTracker.on(`progress:${sessionId}`, progressHandler)
progressTracker.on(`progress:${sessionId}:complete`, completeHandler)
// Cleanup on stream close
request.signal.addEventListener('abort', () => {
progressTracker.off(`progress:${sessionId}`, progressHandler)
progressTracker.off(`progress:${sessionId}:complete`, completeHandler)
controller.close()
})
}
})
return new NextResponse(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
'Access-Control-Allow-Headers': 'Cache-Control'
}
})
}

52
app/api/progress/route.ts Normal file
View File

@@ -0,0 +1,52 @@
import { progressTracker } from '../../../lib/progress-tracker'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const sessionId = searchParams.get('sessionId')
if (!sessionId) {
return new Response('Session ID required', { status: 400 })
}
const stream = new ReadableStream({
start(controller) {
// Send initial progress if session exists
const currentProgress = progressTracker.getProgress(sessionId)
if (currentProgress) {
const data = `data: ${JSON.stringify(currentProgress)}\n\n`
controller.enqueue(new TextEncoder().encode(data))
}
// Listen for progress updates
const progressHandler = (progress: any) => {
const data = `data: ${JSON.stringify(progress)}\n\n`
controller.enqueue(new TextEncoder().encode(data))
}
const completeHandler = () => {
const data = `data: ${JSON.stringify({ type: 'complete' })}\n\n`
controller.enqueue(new TextEncoder().encode(data))
controller.close()
}
progressTracker.on(`progress:${sessionId}`, progressHandler)
progressTracker.on(`progress:${sessionId}:complete`, completeHandler)
// Cleanup on close
return () => {
progressTracker.off(`progress:${sessionId}`, progressHandler)
progressTracker.off(`progress:${sessionId}:complete`, completeHandler)
}
}
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control'
}
})
}

View File

@@ -0,0 +1,230 @@
import { NextResponse } from 'next/server'
import { jupiterDEXService } from '@/lib/jupiter-dex-service'
import { jupiterTriggerService } from '@/lib/jupiter-trigger-service'
export async function POST(request) {
try {
const body = await request.json()
const {
symbol,
side,
amount,
leverage = 1,
stopLoss,
takeProfit,
useRealDEX = false
} = body
console.log('🚀 Jupiter Leveraged Spot Trade request:', {
symbol,
side,
amount,
leverage,
stopLoss,
takeProfit,
useRealDEX
})
// Validate inputs
if (!symbol || !side || !amount) {
return NextResponse.json(
{
success: false,
error: 'Missing required fields: symbol, side, amount'
},
{ status: 400 }
)
}
if (!['BUY', 'SELL'].includes(side.toUpperCase())) {
return NextResponse.json(
{
success: false,
error: 'Invalid side. Must be BUY or SELL'
},
{ status: 400 }
)
}
if (amount <= 0) {
return NextResponse.json(
{
success: false,
error: 'Amount must be greater than 0'
},
{ status: 400 }
)
}
if (leverage < 1 || leverage > 10) {
return NextResponse.json(
{
success: false,
error: 'Leverage must be between 1x and 10x'
},
{ status: 400 }
)
}
if (!useRealDEX) {
// Simulation mode
console.log('🎮 Executing SIMULATED leveraged spot trade')
const currentPrice = symbol === 'SOL' ? 166.75 : symbol === 'BTC' ? 121819 : 3041.66
const leveragedAmount = amount * leverage
const estimatedTokens = side === 'BUY' ? leveragedAmount / currentPrice : leveragedAmount
await new Promise(resolve => setTimeout(resolve, 1200))
return NextResponse.json({
success: true,
trade: {
txId: `jupiter_leverage_sim_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`,
symbol: symbol.toUpperCase(),
side: side.toUpperCase(),
amount: leveragedAmount,
leverage: leverage,
originalAmount: amount,
estimatedTokens: estimatedTokens,
entryPrice: currentPrice,
timestamp: Date.now(),
status: 'FILLED',
platform: 'Jupiter DEX (Leveraged Spot)',
stopLoss: stopLoss,
takeProfit: takeProfit,
triggerOrders: stopLoss || takeProfit ? 'PENDING' : 'NONE'
},
message: `${side.toUpperCase()} ${leveragedAmount} USD worth of ${symbol} (${leverage}x leveraged spot trade) - SIMULATED`
})
}
// Real trading with Jupiter DEX + Trigger Orders
console.log('💰 Executing REAL leveraged spot trade via Jupiter DEX + Trigger Orders')
// Step 1: Execute the main trade with leveraged amount
const leveragedAmount = amount * leverage
const tradingPair = symbol === 'SOL' ? (side === 'BUY' ? 'USDC/SOL' : 'SOL/USDC') : 'SOL/USDC'
const tradeResult = await jupiterDEXService.executeTrade({
symbol,
side,
amount: leveragedAmount,
tradingPair,
quickSwap: false
})
if (!tradeResult.success) {
return NextResponse.json({
success: false,
error: `Main trade failed: ${tradeResult.error}`
}, { status: 400 })
}
console.log('✅ Main leveraged trade executed:', tradeResult.txId)
// Step 2: Calculate position size for trigger orders
const currentPrice = symbol === 'SOL' ? 166.75 : 3041.66 // Get from price API in production
const tokenAmount = side === 'BUY'
? leveragedAmount / currentPrice // If buying SOL, calculate SOL amount
: leveragedAmount // If selling, amount is already in the token
// Step 3: Create trigger orders for stop loss and take profit
let triggerResults = null
if (stopLoss || takeProfit) {
console.log('📋 Creating trigger orders for TP/SL...')
triggerResults = await jupiterTriggerService.createTradingOrders({
tokenSymbol: symbol,
amount: tokenAmount,
stopLoss: stopLoss,
takeProfit: takeProfit,
slippageBps: 50, // 0.5% slippage for trigger orders
expiredAt: Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60) // 30 days expiry
})
if (triggerResults.success) {
console.log('✅ Trigger orders created:', {
stopLoss: triggerResults.stopLossOrder,
takeProfit: triggerResults.takeProfitOrder
})
} else {
console.warn('⚠️ Trigger orders failed:', triggerResults.error)
}
}
// Step 4: Return comprehensive result
const result = {
success: true,
trade: {
txId: tradeResult.txId,
orderId: tradeResult.orderId,
symbol: symbol.toUpperCase(),
side: side.toUpperCase(),
amount: leveragedAmount,
leverage: leverage,
originalAmount: amount,
tokenAmount: tokenAmount,
entryPrice: currentPrice,
timestamp: Date.now(),
status: 'FILLED',
platform: 'Jupiter DEX (Leveraged Spot)',
dex: 'JUPITER_DEX_REAL',
stopLoss: stopLoss,
takeProfit: takeProfit
},
triggerOrders: triggerResults ? {
stopLossOrderId: triggerResults.stopLossOrder,
takeProfitOrderId: triggerResults.takeProfitOrder,
status: triggerResults.success ? 'CREATED' : 'FAILED',
error: triggerResults.error
} : null,
message: `${side.toUpperCase()} $${leveragedAmount} worth of ${symbol} executed successfully`,
explanation: [
`🔥 Leveraged Spot Trade: Used ${leverage}x leverage to trade $${leveragedAmount} instead of $${amount}`,
`💰 Main Trade: ${side === 'BUY' ? 'Bought' : 'Sold'} ~${tokenAmount.toFixed(6)} ${symbol} via Jupiter DEX`,
stopLoss ? `🛑 Stop Loss: Trigger order created at $${stopLoss}` : null,
takeProfit ? `🎯 Take Profit: Trigger order created at $${takeProfit}` : null,
`📈 This gives you ${leverage}x exposure to ${symbol} price movements using spot trading`
].filter(Boolean)
}
return NextResponse.json(result)
} catch (error) {
console.error('❌ Leveraged spot trade execution error:', error)
return NextResponse.json(
{
success: false,
error: 'Internal server error',
message: 'Failed to execute leveraged spot trade. Please try again.'
},
{ status: 500 }
)
}
}
export async function GET() {
return NextResponse.json({
message: 'Jupiter Leveraged Spot Trading API',
description: 'Leveraged trading using Jupiter DEX spot swaps + Trigger Orders for TP/SL',
endpoints: {
'POST /api/trading/execute-leverage': 'Execute leveraged spot trades with trigger orders',
},
features: [
'Leveraged spot trading (1x-10x)',
'Direct wallet trading (no deposits needed)',
'Jupiter Trigger Orders for Stop Loss & Take Profit',
'Real-time execution via Jupiter DEX',
'Automatic position monitoring'
],
advantages: [
'✅ No fund deposits required (unlike Drift)',
'✅ Real leverage effect through increased position size',
'✅ Professional stop loss & take profit via Jupiter Triggers',
'✅ Best execution through Jupiter routing',
'✅ Low fees (0.03% for stables, 0.1% others)'
],
note: 'Uses Jupiter DEX for main trades and Jupiter Trigger API for stop loss/take profit orders.'
})
}