Fix multi-timeframe analysis display and database issues

- Fixed analysis-details API to display multi-timeframe analysis results
- Added comprehensive timeframe breakdown (15m, 1h, 2h, 4h) with confidence levels
- Fixed database field recognition issues with Prisma client
- Enhanced analysis display with entry/exit levels and technical analysis
- Added proper stop loss and take profit calculations from AI analysis
- Improved multi-layout analysis display (AI + DIY layouts)
- Fixed automation service to handle database schema sync issues
- Added detailed momentum, trend, and volume analysis display
- Enhanced decision visibility on automation dashboard
This commit is contained in:
mindesbunister
2025-07-18 22:18:17 +02:00
parent 118e0269f1
commit 9daae9afa1
6 changed files with 745 additions and 130 deletions

View File

@@ -2,6 +2,7 @@ import { PrismaClient } from '@prisma/client'
import { aiAnalysisService, AnalysisResult } from './ai-analysis'
import { jupiterDEXService } from './jupiter-dex-service'
import { enhancedScreenshotService } from './enhanced-screenshot-simple'
import { TradingViewCredentials } from './tradingview-automation'
const prisma = new PrismaClient()
@@ -71,7 +72,16 @@ export class AutomationService {
}
})
// Create automation session in database
// Delete any existing automation session for this user/symbol/timeframe
await prisma.automationSession.deleteMany({
where: {
userId: config.userId,
symbol: config.symbol,
timeframe: config.timeframe
}
})
// Create new automation session in database
await prisma.automationSession.create({
data: {
userId: config.userId,
@@ -163,14 +173,17 @@ export class AutomationService {
// Step 3: Store analysis for learning
await this.storeAnalysisForLearning(analysisResult)
// Step 4: Make trading decision
// Step 4: Update session with latest analysis
await this.updateSessionWithAnalysis(analysisResult)
// Step 5: Make trading decision
const tradeDecision = await this.makeTradeDecision(analysisResult)
if (!tradeDecision) {
console.log('📊 No trading opportunity found')
return
}
// Step 5: Execute trade
// Step 6: Execute trade
await this.executeTrade(tradeDecision)
} catch (error) {
@@ -185,30 +198,206 @@ export class AutomationService {
analysis: AnalysisResult | null
} | null> {
try {
console.log('📸 Taking screenshot and analyzing...')
console.log('📸 Starting multi-timeframe analysis with dual layouts...')
const screenshotConfig = {
symbol: this.config!.symbol,
timeframe: this.config!.timeframe,
layouts: ['ai', 'diy']
// Multi-timeframe analysis: 15m, 1h, 2h, 4h
const timeframes = ['15', '1h', '2h', '4h']
const symbol = this.config!.symbol
console.log(`🔍 Analyzing ${symbol} across timeframes: ${timeframes.join(', ')} with AI + DIY layouts`)
// Analyze each timeframe with both AI and DIY layouts
const multiTimeframeResults = await this.analyzeMultiTimeframeWithDualLayouts(symbol, timeframes)
if (multiTimeframeResults.length === 0) {
console.log('❌ No multi-timeframe analysis results')
return null
}
const result = await aiAnalysisService.captureAndAnalyzeWithConfig(screenshotConfig)
if (!result.analysis || result.screenshots.length === 0) {
console.log('❌ No analysis or screenshots captured')
// Process and combine multi-timeframe results
const combinedResult = this.combineMultiTimeframeAnalysis(multiTimeframeResults)
if (!combinedResult.analysis) {
console.log('❌ Failed to combine multi-timeframe analysis')
return null
}
console.log(`Analysis completed: ${result.analysis.recommendation} with ${result.analysis.confidence}% confidence`)
return result
console.log(`Multi-timeframe analysis completed: ${combinedResult.analysis.recommendation} with ${combinedResult.analysis.confidence}% confidence`)
console.log(`📊 Timeframe alignment: ${this.analyzeTimeframeAlignment(multiTimeframeResults)}`)
return combinedResult
} catch (error) {
console.error('Error performing analysis:', error)
console.error('Error performing multi-timeframe analysis:', error)
return null
}
}
private async analyzeMultiTimeframeWithDualLayouts(
symbol: string,
timeframes: string[]
): Promise<Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }>> {
const results: Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }> = []
for (const timeframe of timeframes) {
try {
console.log(`📊 Analyzing ${symbol} ${timeframe} with AI + DIY layouts...`)
// Use the dual-layout configuration for each timeframe
const screenshotConfig = {
symbol: symbol,
timeframe: timeframe,
layouts: ['ai', 'diy']
}
const result = await aiAnalysisService.captureAndAnalyzeWithConfig(screenshotConfig)
if (result.analysis) {
console.log(`${timeframe} analysis: ${result.analysis.recommendation} (${result.analysis.confidence}% confidence)`)
results.push({
symbol,
timeframe,
analysis: result.analysis
})
} else {
console.log(`${timeframe} analysis failed`)
results.push({
symbol,
timeframe,
analysis: null
})
}
// Small delay between captures to avoid overwhelming the system
await new Promise(resolve => setTimeout(resolve, 3000))
} catch (error) {
console.error(`Failed to analyze ${symbol} ${timeframe}:`, error)
results.push({
symbol,
timeframe,
analysis: null
})
}
}
return results
}
private combineMultiTimeframeAnalysis(results: Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }>): {
screenshots: string[]
analysis: AnalysisResult | null
} {
const validResults = results.filter(r => r.analysis !== null)
if (validResults.length === 0) {
return { screenshots: [], analysis: null }
}
// Get the primary timeframe (1h) as base
const primaryResult = validResults.find(r => r.timeframe === '1h') || validResults[0]
const screenshots = validResults.length > 0 ? [primaryResult.timeframe] : []
// Calculate weighted confidence based on timeframe alignment
const alignment = this.calculateTimeframeAlignment(validResults)
const baseAnalysis = primaryResult.analysis!
// Adjust confidence based on timeframe alignment
const adjustedConfidence = Math.round(baseAnalysis.confidence * alignment.score)
// Create combined analysis with multi-timeframe reasoning
const combinedAnalysis: AnalysisResult = {
...baseAnalysis,
confidence: adjustedConfidence,
reasoning: `Multi-timeframe Dual-Layout Analysis (${results.map(r => r.timeframe).join(', ')}): ${baseAnalysis.reasoning}
📊 Each timeframe analyzed with BOTH AI layout (RSI, MACD, EMAs) and DIY layout (Stochastic RSI, VWAP, OBV)
⏱️ Timeframe Alignment: ${alignment.description}
<EFBFBD> Signal Strength: ${alignment.strength}
🎯 Confidence Adjustment: ${baseAnalysis.confidence}% → ${adjustedConfidence}% (${alignment.score >= 1 ? 'Enhanced' : 'Reduced'} due to timeframe ${alignment.score >= 1 ? 'alignment' : 'divergence'})
🔬 Analysis Details:
${validResults.map(r => `${r.timeframe}: ${r.analysis?.recommendation} (${r.analysis?.confidence}% confidence)`).join('\n')}`,
keyLevels: this.consolidateKeyLevels(validResults),
marketSentiment: this.consolidateMarketSentiment(validResults)
}
return {
screenshots,
analysis: combinedAnalysis
}
}
private calculateTimeframeAlignment(results: Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }>): {
score: number
description: string
strength: string
} {
const recommendations = results.map(r => r.analysis?.recommendation).filter(Boolean)
const buySignals = recommendations.filter(r => r === 'BUY').length
const sellSignals = recommendations.filter(r => r === 'SELL').length
const holdSignals = recommendations.filter(r => r === 'HOLD').length
const total = recommendations.length
const maxSignals = Math.max(buySignals, sellSignals, holdSignals)
const alignmentRatio = maxSignals / total
let score = 1.0
let description = ''
let strength = ''
if (alignmentRatio >= 0.75) {
score = 1.2 // Boost confidence
description = `Strong alignment (${maxSignals}/${total} timeframes agree)`
strength = 'Strong'
} else if (alignmentRatio >= 0.5) {
score = 1.0 // Neutral
description = `Moderate alignment (${maxSignals}/${total} timeframes agree)`
strength = 'Moderate'
} else {
score = 0.8 // Reduce confidence
description = `Weak alignment (${maxSignals}/${total} timeframes agree)`
strength = 'Weak'
}
return { score, description, strength }
}
private consolidateKeyLevels(results: Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }>): any {
const allLevels = results.map(r => r.analysis?.keyLevels).filter(Boolean)
if (allLevels.length === 0) return {}
// Use the 1h timeframe levels as primary, or first available
const primaryLevels = results.find(r => r.timeframe === '1h')?.analysis?.keyLevels || allLevels[0]
return {
...primaryLevels,
note: `Consolidated from ${allLevels.length} timeframes`
}
}
private consolidateMarketSentiment(results: Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }>): 'BULLISH' | 'BEARISH' | 'NEUTRAL' {
const sentiments = results.map(r => r.analysis?.marketSentiment).filter(Boolean)
if (sentiments.length === 0) return 'NEUTRAL'
// Use the 1h timeframe sentiment as primary, or first available
const primarySentiment = results.find(r => r.timeframe === '1h')?.analysis?.marketSentiment || sentiments[0]
return primarySentiment || 'NEUTRAL'
}
private analyzeTimeframeAlignment(results: Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }>): string {
const recommendations = results.map(r => ({
timeframe: r.timeframe,
recommendation: r.analysis?.recommendation,
confidence: r.analysis?.confidence || 0
}))
const summary = recommendations.map(r => `${r.timeframe}: ${r.recommendation} (${r.confidence}%)`).join(', ')
return summary
}
private async storeAnalysisForLearning(result: {
screenshots: string[]
analysis: AnalysisResult | null
@@ -237,6 +426,41 @@ export class AutomationService {
}
}
private async updateSessionWithAnalysis(result: {
screenshots: string[]
analysis: AnalysisResult | null
}): Promise<void> {
try {
if (!result.analysis) return
// Store the analysis decision in a field that works for now
const analysisDecision = `${result.analysis.recommendation} with ${result.analysis.confidence}% confidence - ${result.analysis.summary}`
// Update the current session with the latest analysis
await prisma.automationSession.updateMany({
where: {
userId: this.config!.userId,
symbol: this.config!.symbol,
timeframe: this.config!.timeframe,
status: 'ACTIVE'
},
data: {
lastAnalysis: new Date(),
lastError: analysisDecision // Temporarily store analysis here
}
})
// Also log the analysis for debugging
console.log('📊 Analysis stored in database:', {
recommendation: result.analysis.recommendation,
confidence: result.analysis.confidence,
summary: result.analysis.summary
})
} catch (error) {
console.error('Failed to update session with analysis:', error)
}
}
private async makeTradeDecision(result: {
screenshots: string[]
analysis: AnalysisResult | null
@@ -284,10 +508,15 @@ export class AutomationService {
}
private calculateStopLoss(analysis: any): number {
const currentPrice = analysis.currentPrice || 0
// Use AI analysis stopLoss if available, otherwise calculate from entry price
if (analysis.stopLoss?.price) {
return analysis.stopLoss.price
}
const currentPrice = analysis.entry?.price || 150 // Default SOL price
const stopLossPercent = this.config!.stopLossPercent / 100
if (analysis.direction === 'LONG') {
if (analysis.recommendation === 'BUY') {
return currentPrice * (1 - stopLossPercent)
} else {
return currentPrice * (1 + stopLossPercent)
@@ -295,10 +524,15 @@ export class AutomationService {
}
private calculateTakeProfit(analysis: any): number {
const currentPrice = analysis.currentPrice || 0
// Use AI analysis takeProfit if available, otherwise calculate from entry price
if (analysis.takeProfits?.tp1?.price) {
return analysis.takeProfits.tp1.price
}
const currentPrice = analysis.entry?.price || 150 // Default SOL price
const takeProfitPercent = this.config!.takeProfitPercent / 100
if (analysis.direction === 'LONG') {
if (analysis.recommendation === 'BUY') {
return currentPrice * (1 + takeProfitPercent)
} else {
return currentPrice * (1 - takeProfitPercent)
@@ -429,6 +663,29 @@ export class AutomationService {
async stopAutomation(): Promise<boolean> {
try {
this.isRunning = false
// Clear the interval if it exists
if (this.intervalId) {
clearInterval(this.intervalId)
this.intervalId = null
}
// Update database session status to STOPPED
if (this.config) {
await prisma.automationSession.updateMany({
where: {
userId: this.config.userId,
symbol: this.config.symbol,
timeframe: this.config.timeframe,
status: 'ACTIVE'
},
data: {
status: 'STOPPED',
updatedAt: new Date()
}
})
}
this.config = null
console.log('🛑 Automation stopped')
@@ -471,23 +728,35 @@ export class AutomationService {
async getStatus(): Promise<AutomationStatus | null> {
try {
if (!this.config) {
// If automation is not running in memory, return null regardless of database state
if (!this.isRunning || !this.config) {
return null
}
// Get the latest active automation session from database
const session = await prisma.automationSession.findFirst({
where: { status: 'ACTIVE' },
orderBy: { createdAt: 'desc' }
})
if (!session) {
return null
}
return {
isActive: this.isRunning,
mode: this.config.mode,
symbol: this.config.symbol,
timeframe: this.config.timeframe,
totalTrades: this.stats.totalTrades,
successfulTrades: this.stats.successfulTrades,
winRate: this.stats.winRate,
totalPnL: this.stats.totalPnL,
errorCount: this.stats.errorCount,
lastError: this.stats.lastError || undefined,
lastAnalysis: new Date(),
lastTrade: new Date()
mode: session.mode as 'SIMULATION' | 'LIVE',
symbol: session.symbol,
timeframe: session.timeframe,
totalTrades: session.totalTrades,
successfulTrades: session.successfulTrades,
winRate: session.winRate,
totalPnL: session.totalPnL,
errorCount: session.errorCount,
lastError: session.lastError || undefined,
lastAnalysis: session.lastAnalysis || undefined,
lastTrade: session.lastTrade || undefined,
nextScheduled: session.nextScheduled || undefined
}
} catch (error) {
console.error('Failed to get automation status:', error)