diff --git a/app/api/automation/analysis-details/route.js b/app/api/automation/analysis-details/route.js index 626ae2c..ee9b51c 100644 --- a/app/api/automation/analysis-details/route.js +++ b/app/api/automation/analysis-details/route.js @@ -40,7 +40,37 @@ export async function GET() { aiAnalysis: 'BUY signal with 78% confidence - Multi-timeframe bullish alignment', stopLoss: 172.50, takeProfit: 178.00, - confidence: 78 + confidence: 78, + // Enhanced analysis context + triggerAnalysis: { + decision: 'BUY', + confidence: 78, + timeframe: '1h', + keySignals: ['RSI oversold (28)', 'MACD bullish crossover', 'Support bounce at 174.00'], + marketCondition: 'Bullish reversal pattern', + riskReward: '1:2.2', + invalidationLevel: 172.00 + }, + // Current trade metrics + currentMetrics: { + currentPrice: 175.82, + priceChange: 1.57, + priceChangePercent: 0.90, + timeInTrade: '30 minutes', + unrealizedPnL: 2.35, + unrealizedPnLPercent: 1.35, + distanceToSL: 3.32, + distanceToTP: 2.18, + riskRewardActual: '1:1.4' + }, + // Exit conditions + exitConditions: { + stopLossHit: false, + takeProfitHit: false, + manualExit: false, + timeBasedExit: false, + analysisInvalidated: false + } }, { id: 'demo-trade-2', @@ -53,7 +83,35 @@ export async function GET() { aiAnalysis: 'SELL signal with 85% confidence - Resistance level rejection', stopLoss: 178.50, takeProfit: 174.20, - confidence: 85 + confidence: 85, + // Enhanced analysis context + triggerAnalysis: { + decision: 'SELL', + confidence: 85, + timeframe: '1h', + keySignals: ['RSI overbought (72)', 'Resistance rejection at 177.00', 'Bearish divergence'], + marketCondition: 'Distribution at resistance', + riskReward: '1:1.6', + invalidationLevel: 179.00 + }, + // Exit metrics + exitMetrics: { + exitPrice: 174.20, + exitReason: 'Take profit hit', + timeInTrade: '85 minutes', + maxUnrealizedPnL: 4.15, + maxDrawdown: -0.85, + analysisAccuracy: 'Excellent - TP hit exactly', + actualRiskReward: '1:1.6' + }, + // Exit conditions + exitConditions: { + stopLossHit: false, + takeProfitHit: true, + manualExit: false, + timeBasedExit: false, + analysisInvalidated: false + } }, { id: 'demo-trade-3', @@ -66,7 +124,35 @@ export async function GET() { aiAnalysis: 'BUY signal with 72% confidence - Support level bounce', stopLoss: 171.80, takeProfit: 176.50, - confidence: 72 + confidence: 72, + // Enhanced analysis context + triggerAnalysis: { + decision: 'BUY', + confidence: 72, + timeframe: '1h', + keySignals: ['Support test at 173.00', 'Bullish hammer candle', 'Volume spike'], + marketCondition: 'Support bounce attempt', + riskReward: '1:2.5', + invalidationLevel: 171.50 + }, + // Exit metrics + exitMetrics: { + exitPrice: 171.80, + exitReason: 'Stop loss hit', + timeInTrade: '45 minutes', + maxUnrealizedPnL: 0.85, + maxDrawdown: -1.89, + analysisAccuracy: 'Poor - Support failed to hold', + actualRiskReward: '1:0' + }, + // Exit conditions + exitConditions: { + stopLossHit: true, + takeProfitHit: false, + manualExit: false, + timeBasedExit: false, + analysisInvalidated: true + } } ] @@ -99,12 +185,21 @@ export async function GET() { summary: "Multi-timeframe analysis completed: HOLD with 84% confidence. 📊 Timeframe alignment: 15: HOLD (75%), 1h: HOLD (70%), 2h: HOLD (70%), 4h: HOLD (70%)", sentiment: "NEUTRAL", + // Analysis context - why HOLD when there's an active BUY trade + analysisContext: { + currentSignal: "HOLD", + explanation: "Current analysis shows HOLD signal, but there's an active BUY trade from 30 minutes ago when analysis was BUY (78% confidence). The market has moved into a neutral zone since then.", + previousSignal: "BUY", + signalChange: "BUY → HOLD", + marketEvolution: "Market moved from bullish setup to neutral consolidation" + }, + // Multi-timeframe breakdown timeframeAnalysis: { - "15m": { decision: "HOLD", confidence: 75 }, - "1h": { decision: "HOLD", confidence: 70 }, - "2h": { decision: "HOLD", confidence: 70 }, - "4h": { decision: "HOLD", confidence: 70 } + "15m": { decision: "HOLD", confidence: 75, change: "BUY → HOLD" }, + "1h": { decision: "HOLD", confidence: 70, change: "BUY → HOLD" }, + "2h": { decision: "HOLD", confidence: 70, change: "NEUTRAL → HOLD" }, + "4h": { decision: "HOLD", confidence: 70, change: "NEUTRAL → HOLD" } }, // Layout information @@ -114,7 +209,7 @@ export async function GET() { entry: { price: 175.82, buffer: "±0.25", - rationale: "Current price is at a neutral level with no strong signals for entry." + rationale: "Current price is at a neutral level with no strong signals for new entries." }, stopLoss: { price: 174.5, @@ -131,7 +226,7 @@ export async function GET() { } }, - reasoning: "Multi-timeframe Dual-Layout Analysis (15, 1h, 2h, 4h): All timeframes show HOLD signals with strong alignment. No clear directional bias detected across layouts.", + reasoning: "Multi-timeframe Dual-Layout Analysis (15, 1h, 2h, 4h): All timeframes show HOLD signals with strong alignment. Previous BUY signal (30 min ago) has evolved into neutral territory. Active trade is being monitored for exit signals.", // Technical analysis momentumAnalysis: { @@ -179,32 +274,73 @@ export async function GET() { pnlPercent: trade.profit ? ((trade.profit / (trade.amount * trade.price)) * 100).toFixed(2) + '%' : null, createdAt: trade.createdAt, reason: trade.aiAnalysis || `${trade.side} signal with confidence`, + // Enhanced trade details entryPrice: trade.price, - currentPrice: trade.status === 'OPEN' ? - (trade.side === 'BUY' ? 175.82 : 175.82) : trade.price, // Use current market price for open trades + currentPrice: trade.status === 'OPEN' ? 175.82 : (trade.exitMetrics?.exitPrice || trade.price), unrealizedPnl: trade.status === 'OPEN' ? (trade.side === 'BUY' ? ((175.82 - trade.price) * trade.amount).toFixed(2) : ((trade.price - 175.82) * trade.amount).toFixed(2)) : null, duration: trade.status === 'COMPLETED' ? - `${Math.floor((Date.now() - new Date(trade.createdAt).getTime()) / (1000 * 60))} minutes` : + (trade.exitMetrics?.timeInTrade || `${Math.floor((Date.now() - new Date(trade.createdAt).getTime()) / (1000 * 60))} minutes`) : `${Math.floor((Date.now() - new Date(trade.createdAt).getTime()) / (1000 * 60))} minutes (Active)`, stopLoss: trade.stopLoss || (trade.side === 'BUY' ? (trade.price * 0.98).toFixed(2) : (trade.price * 1.02).toFixed(2)), takeProfit: trade.takeProfit || (trade.side === 'BUY' ? (trade.price * 1.04).toFixed(2) : (trade.price * 0.96).toFixed(2)), isActive: trade.status === 'OPEN' || trade.status === 'PENDING', confidence: trade.confidence || 102, + + // Enhanced analysis context + triggerAnalysis: trade.triggerAnalysis ? { + decision: trade.triggerAnalysis.decision, + confidence: trade.triggerAnalysis.confidence, + timeframe: trade.triggerAnalysis.timeframe, + keySignals: trade.triggerAnalysis.keySignals, + marketCondition: trade.triggerAnalysis.marketCondition, + riskReward: trade.triggerAnalysis.riskReward, + invalidationLevel: trade.triggerAnalysis.invalidationLevel + } : null, + + // Current trade metrics (for active trades) + currentMetrics: trade.currentMetrics ? { + currentPrice: trade.currentMetrics.currentPrice, + priceChange: trade.currentMetrics.priceChange, + priceChangePercent: trade.currentMetrics.priceChangePercent, + timeInTrade: trade.currentMetrics.timeInTrade, + unrealizedPnL: trade.currentMetrics.unrealizedPnL, + unrealizedPnLPercent: trade.currentMetrics.unrealizedPnLPercent, + distanceToSL: trade.currentMetrics.distanceToSL, + distanceToTP: trade.currentMetrics.distanceToTP, + riskRewardActual: trade.currentMetrics.riskRewardActual + } : null, + + // Exit metrics (for completed trades) + exitMetrics: trade.exitMetrics ? { + exitPrice: trade.exitMetrics.exitPrice, + exitReason: trade.exitMetrics.exitReason, + timeInTrade: trade.exitMetrics.timeInTrade, + maxUnrealizedPnL: trade.exitMetrics.maxUnrealizedPnL, + maxDrawdown: trade.exitMetrics.maxDrawdown, + analysisAccuracy: trade.exitMetrics.analysisAccuracy, + actualRiskReward: trade.exitMetrics.actualRiskReward + } : null, + + // Exit conditions + exitConditions: trade.exitConditions ? { + stopLossHit: trade.exitConditions.stopLossHit, + takeProfitHit: trade.exitConditions.takeProfitHit, + manualExit: trade.exitConditions.manualExit, + timeBasedExit: trade.exitConditions.timeBasedExit, + analysisInvalidated: trade.exitConditions.analysisInvalidated + } : null, + // Trade result analysis result: trade.status === 'COMPLETED' ? (trade.profit > 0 ? 'PROFIT' : trade.profit < 0 ? 'LOSS' : 'BREAKEVEN') : 'ACTIVE', resultDescription: trade.status === 'COMPLETED' ? - `${trade.profit > 0 ? 'Successful' : 'Losing'} ${trade.side} trade - ${trade.profit > 0 ? '+' : ''}${trade.profit}` : - `${trade.side} position active - Current P&L: ${trade.status === 'OPEN' ? - (trade.side === 'BUY' ? - ((175.82 - trade.price) * trade.amount > 0 ? '+' : '') + ((175.82 - trade.price) * trade.amount).toFixed(2) : - ((trade.price - 175.82) * trade.amount > 0 ? '+' : '') + ((trade.price - 175.82) * trade.amount).toFixed(2)) : - 'N/A'}` + `${trade.profit > 0 ? 'Successful' : 'Failed'} ${trade.side} trade - ${trade.exitMetrics?.exitReason || 'Completed'}` : + `${trade.side} position active - ${trade.currentMetrics?.timeInTrade || 'Active'}` })) } }) diff --git a/app/automation/page.js b/app/automation/page.js index 6323c03..f17fe7a 100644 --- a/app/automation/page.js +++ b/app/automation/page.js @@ -475,18 +475,19 @@ export default function AutomationPage() {

Recent Automated Trades

{recentTrades.length > 0 ? ( -
+
{recentTrades.slice(0, 5).map((trade, idx) => ( -
-
-
+
+ {/* Trade Header */} +
+
{trade.side} - {trade.amount} - {trade.amount} + {trade.confidence}% confidence
-
- {trade.reason} -
-
-
- {trade.duration} -
-
0 ? 'text-green-400' : 'text-red-400') : - (trade.pnl > 0 ? 'text-green-400' : 'text-red-400') - }`}> - {trade.isActive ? - `P&L: ${trade.unrealizedPnl > 0 ? '+' : ''}${trade.unrealizedPnl}` : - `P&L: ${trade.pnl > 0 ? '+' : ''}${trade.pnl}` - } -
-
- {trade.isActive && ( -
-
- SL: ${trade.stopLoss} - Current: ${trade.currentPrice.toFixed(2)} - TP: ${trade.takeProfit} + + {/* Analysis Context */} + {trade.triggerAnalysis && ( +
+

📊 Trigger Analysis

+
+
+ Decision: + {trade.triggerAnalysis.decision} ({trade.triggerAnalysis.confidence}%) +
+
+ Market Condition: + {trade.triggerAnalysis.marketCondition} +
+
+ Expected R/R: + {trade.triggerAnalysis.riskReward} +
+
+ Key Signals: +
    + {trade.triggerAnalysis.keySignals.map((signal, signalIdx) => ( +
  • • {signal}
  • + ))} +
+
)} + + {/* Current Metrics (Active Trades) */} + {trade.isActive && trade.currentMetrics && ( +
+

📈 Current Status

+
+
+ Current Price: + ${trade.currentMetrics.currentPrice.toFixed(2)} +
+
+ Price Change: + 0 ? 'text-green-400' : 'text-red-400'}`}> + {trade.currentMetrics.priceChange > 0 ? '+' : ''}${trade.currentMetrics.priceChange.toFixed(2)} + +
+
+ Unrealized P&L: + 0 ? 'text-green-400' : 'text-red-400'}`}> + {trade.currentMetrics.unrealizedPnL > 0 ? '+' : ''}${trade.currentMetrics.unrealizedPnL.toFixed(2)} + +
+
+ Time in Trade: + {trade.currentMetrics.timeInTrade} +
+
+
+ )} + + {/* Exit Metrics (Completed Trades) */} + {!trade.isActive && trade.exitMetrics && ( +
+

📊 Exit Analysis

+
+
+ Exit Reason: + {trade.exitMetrics.exitReason} +
+
+ Exit Price: + ${trade.exitMetrics.exitPrice.toFixed(2)} +
+
+ Analysis Accuracy: + + {trade.exitMetrics.analysisAccuracy} + +
+
+ Actual R/R: + {trade.exitMetrics.actualRiskReward} +
+
+
+ )} + + {/* Trade Summary */} +
+
+ {trade.duration} +
+
+
+ SL: ${trade.stopLoss} | TP: ${trade.takeProfit} +
+
0 ? 'text-green-400' : 'text-red-400') : + (trade.pnl > 0 ? 'text-green-400' : 'text-red-400') + }`}> + {trade.isActive ? + `P&L: ${trade.unrealizedPnl > 0 ? '+' : ''}${trade.unrealizedPnl}` : + `P&L: ${trade.pnl > 0 ? '+' : ''}${trade.pnl}` + } +
+
+
))}
diff --git a/lib/automation-service-simple.ts b/lib/automation-service-simple.ts index 62be660..f9a386b 100644 --- a/lib/automation-service-simple.ts +++ b/lib/automation-service-simple.ts @@ -728,12 +728,7 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r. async getStatus(): Promise { try { - // 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 + // Get the latest active automation session from database first const session = await prisma.automationSession.findFirst({ where: { status: 'ACTIVE' }, orderBy: { createdAt: 'desc' } @@ -743,8 +738,18 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r. return null } + // If we have a session but automation is not running in memory, + // it means the server was restarted but the session is still active + const isActiveInMemory = this.isRunning && this.config !== null + + // Auto-restart automation if session exists but not running in memory + if (!isActiveInMemory) { + console.log('🔄 Found active session but automation not running, attempting auto-restart...') + await this.autoRestartFromSession(session) + } + return { - isActive: this.isRunning, + isActive: this.isRunning && this.config !== null, mode: session.mode as 'SIMULATION' | 'LIVE', symbol: session.symbol, timeframe: session.timeframe, @@ -764,6 +769,29 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r. } } + private async autoRestartFromSession(session: any): Promise { + try { + const settings = session.settings || {} + const config: AutomationConfig = { + userId: session.userId, + mode: session.mode, + symbol: session.symbol, + timeframe: session.timeframe, + tradingAmount: settings.tradingAmount || 100, + maxLeverage: settings.maxLeverage || 3, + stopLossPercent: settings.stopLossPercent || 2, + takeProfitPercent: settings.takeProfitPercent || 6, + maxDailyTrades: settings.maxDailyTrades || 5, + riskPercentage: settings.riskPercentage || 2 + } + + await this.startAutomation(config) + console.log('✅ Automation auto-restarted successfully') + } catch (error) { + console.error('Failed to auto-restart automation:', error) + } + } + async getLearningInsights(userId: string): Promise<{ totalAnalyses: number avgAccuracy: number