diff --git a/app/api/learning/persistent-status/route.js b/app/api/learning/persistent-status/route.js index cb3b04b..861d0ad 100644 --- a/app/api/learning/persistent-status/route.js +++ b/app/api/learning/persistent-status/route.js @@ -1,116 +1,163 @@ -// API route for persistent learning data that works regardless of automation status import { NextResponse } from 'next/server'; -import { PrismaClient } from '@prisma/client'; +import fs from 'fs/promises'; +import path from 'path'; -const prisma = new PrismaClient(); +const PERSISTENT_DATA_FILE = path.join(process.cwd(), 'data', 'learning-persistent.json'); + +// Default persistent data structure +const defaultPersistentData = { + totalTrades: 0, + winningTrades: 0, + losingTrades: 0, + totalPnL: 0, + winRate: 0, + avgWinAmount: 0, + avgLossAmount: 0, + bestTrade: 0, + worstTrade: 0, + learningDecisions: 0, + aiEnhancements: 0, + riskThresholds: { + emergency: 1, + risk: 2, + mediumRisk: 5 + }, + lastUpdated: new Date().toISOString(), + systemStatus: 'learning', + dataCollected: true +}; + +async function ensureDataDirectory() { + const dataDir = path.join(process.cwd(), 'data'); + try { + await fs.access(dataDir); + } catch { + await fs.mkdir(dataDir, { recursive: true }); + } +} + +async function loadPersistentData() { + try { + await ensureDataDirectory(); + const data = await fs.readFile(PERSISTENT_DATA_FILE, 'utf8'); + return JSON.parse(data); + } catch (error) { + // File doesn't exist or is invalid, return default data + return defaultPersistentData; + } +} + +async function savePersistentData(data) { + try { + await ensureDataDirectory(); + await fs.writeFile(PERSISTENT_DATA_FILE, JSON.stringify(data, null, 2)); + return true; + } catch (error) { + console.error('Error saving persistent data:', error); + return false; + } +} export async function GET() { try { - // Get persistent learning statistics from database using raw SQL - const [ - totalDecisions, - recentDecisions, - totalTrades, - successfulTrades, - recentTrades - ] = await Promise.all([ - // Total AI decisions count - prisma.$queryRaw`SELECT COUNT(*) as count FROM ai_learning_data`, - - // Recent decisions (last 24 hours) - prisma.$queryRaw`SELECT COUNT(*) as count FROM ai_learning_data WHERE createdAt >= datetime('now', '-24 hours')`, - - // Total trades - prisma.$queryRaw`SELECT COUNT(*) as count FROM trades`, - - // Successful trades (profit > 0) - prisma.$queryRaw`SELECT COUNT(*) as count FROM trades WHERE profit > 0`, - - // Recent trades with PnL data - prisma.$queryRaw` - SELECT id, symbol, profit, side, confidence, marketSentiment, createdAt, closedAt, status - FROM trades - WHERE profit IS NOT NULL AND status = 'COMPLETED' - ORDER BY createdAt DESC - LIMIT 10 - ` - ]); - - // Extract counts (BigInt to Number) - const totalDecisionsCount = Number(totalDecisions[0]?.count || 0); - const recentDecisionsCount = Number(recentDecisions[0]?.count || 0); - const totalTradesCount = Number(totalTrades[0]?.count || 0); - const successfulTradesCount = Number(successfulTrades[0]?.count || 0); - - // Calculate metrics - const successRate = totalTradesCount > 0 ? (successfulTradesCount / totalTradesCount) * 100 : 0; - const totalPnl = recentTrades.reduce((sum, trade) => sum + (Number(trade.profit) || 0), 0); - const avgPnl = recentTrades.length > 0 ? totalPnl / recentTrades.length : 0; + const persistentData = await loadPersistentData(); - // Get wins and losses - const wins = recentTrades.filter(trade => Number(trade.profit) > 0).length; - const losses = recentTrades.filter(trade => Number(trade.profit) < 0).length; - - const persistentData = { - success: true, - statistics: { - totalDecisions: totalDecisionsCount, - recentDecisions: recentDecisionsCount, - totalTrades: totalTradesCount, - successfulTrades: successfulTradesCount, - successRate: Math.round(successRate * 100) / 100, - totalPnl: Math.round(totalPnl * 100) / 100, - avgPnl: Math.round(avgPnl * 100) / 100, - wins, - losses, - winRate: wins + losses > 0 ? Math.round((wins / (wins + losses)) * 100 * 100) / 100 : 0 - }, - recentTrades: recentTrades.map(trade => ({ - id: trade.id, - symbol: trade.symbol, - pnl: Number(trade.profit), - result: Number(trade.profit) > 0 ? 'WIN' : 'LOSS', - confidence: trade.confidence, - side: trade.side, - sentiment: trade.marketSentiment, - date: trade.createdAt, - closedAt: trade.closedAt - })), - systemHealth: { - dataAvailability: totalDecisionsCount > 0 ? 'Good' : 'Limited', - lastActivity: recentTrades.length > 0 ? recentTrades[0].createdAt : null, - databaseConnected: true, - activeDataSources: { - aiDecisions: totalDecisionsCount, - completedTrades: totalTradesCount, - recentActivity: recentDecisionsCount + // Get current automation status if available + let currentStatus = null; + try { + const { getAutomationInstance } = await import('../../../../lib/automation-singleton.js'); + const automation = await getAutomationInstance(); + if (automation) { + currentStatus = automation.getStatus(); + + // If automation has learning status, get it too + if (typeof automation.getLearningStatus === 'function') { + const learningStatus = await automation.getLearningStatus(); + if (learningStatus && learningStatus.report) { + // Update some data from current learning status + persistentData.lastUpdated = new Date().toISOString(); + persistentData.systemStatus = learningStatus.enabled ? 'active' : 'standby'; + } } } - }; + } catch (error) { + console.warn('Could not get current automation status:', error.message); + } - return NextResponse.json(persistentData); + return NextResponse.json({ + success: true, + persistentData: { + ...persistentData, + isLive: currentStatus?.isActive || false, + currentRunTime: currentStatus?.startTime || null, + enhancedSummary: { + totalDecisions: persistentData.learningDecisions, + successRate: persistentData.winRate, + systemConfidence: persistentData.winRate > 60 ? 0.8 : persistentData.winRate > 40 ? 0.6 : 0.3, + isActive: persistentData.systemStatus === 'active', + totalTrades: persistentData.totalTrades, + totalPnL: persistentData.totalPnL + }, + tradingStats: { + totalTrades: persistentData.totalTrades, + winningTrades: persistentData.winningTrades, + losingTrades: persistentData.losingTrades, + winRate: persistentData.winRate, + totalPnL: persistentData.totalPnL, + avgWinAmount: persistentData.avgWinAmount, + avgLossAmount: persistentData.avgLossAmount, + bestTrade: persistentData.bestTrade, + worstTrade: persistentData.worstTrade + }, + learningMetrics: { + totalDecisions: persistentData.learningDecisions, + aiEnhancements: persistentData.aiEnhancements, + riskThresholds: persistentData.riskThresholds, + dataQuality: persistentData.totalTrades > 10 ? 'Good' : persistentData.totalTrades > 5 ? 'Fair' : 'Limited' + } + } + }); } catch (error) { - console.error('❌ Error fetching persistent learning data:', error); - + console.error('Error in persistent status API:', error); return NextResponse.json({ success: false, error: error.message, - statistics: { - totalDecisions: 0, - totalTrades: 0, - successRate: 0, - totalPnl: 0, - wins: 0, - losses: 0, - winRate: 0 - }, - systemHealth: { - dataAvailability: 'Error', - databaseConnected: false - } + persistentData: defaultPersistentData }, { status: 500 }); - } finally { - await prisma.$disconnect(); } } + +export async function POST(request) { + try { + const updateData = await request.json(); + const currentData = await loadPersistentData(); + + // Update the persistent data + const updatedData = { + ...currentData, + ...updateData, + lastUpdated: new Date().toISOString() + }; + + // Recalculate derived metrics + if (updatedData.totalTrades > 0) { + updatedData.winRate = (updatedData.winningTrades / updatedData.totalTrades) * 100; + } + + const saved = await savePersistentData(updatedData); + + return NextResponse.json({ + success: saved, + message: saved ? 'Persistent data updated' : 'Failed to save data', + data: updatedData + }); + + } catch (error) { + console.error('Error updating persistent data:', error); + return NextResponse.json({ + success: false, + error: error.message + }, { status: 500 }); + } +} \ No newline at end of file diff --git a/components/EnhancedAILearningPanel.tsx b/components/EnhancedAILearningPanel.tsx index 8ed146c..dc66ef7 100644 --- a/components/EnhancedAILearningPanel.tsx +++ b/components/EnhancedAILearningPanel.tsx @@ -1,6 +1,4 @@ import React, { useState, useEffect } from 'react'; -import { motion } from 'framer-motion'; -import { TrendingUp } from 'lucide-react'; interface LearningData { learningSystem: { @@ -9,23 +7,6 @@ interface LearningData { activeDecisions?: number; message?: string; recommendation?: string; - persistentStats?: { - totalTrades: number; - successRate: number; - totalPnl: number; - winRate: number; - }; - recentTrades?: Array<{ - symbol: string; - type: string; - pnl: string; - updatedAt: string; - }>; - systemHealth?: { - totalDecisions: number; - recentDecisions: number; - lastActivity: string; - }; report?: { summary?: { totalDecisions?: number; @@ -50,6 +31,35 @@ interface LearningData { lastUpdateTime?: string; }; automationStatus?: any; + persistentData?: { + tradingStats?: { + totalTrades?: number; + winningTrades?: number; + losingTrades?: number; + winRate?: number; + totalPnL?: number; + avgWinAmount?: number; + avgLossAmount?: number; + bestTrade?: number; + worstTrade?: number; + }; + enhancedSummary?: { + totalDecisions?: number; + successRate?: number; + systemConfidence?: number; + isActive?: boolean; + totalTrades?: number; + totalPnL?: number; + }; + learningMetrics?: { + totalDecisions?: number; + aiEnhancements?: number; + riskThresholds?: any; + dataQuality?: string; + }; + isLive?: boolean; + currentRunTime?: string; + } | null; } const EnhancedAILearningPanel = () => { @@ -61,7 +71,7 @@ const EnhancedAILearningPanel = () => { try { setLoading(true); - // Get both learning status and persistent data (regardless of automation status) + // Get learning status, automation status, and persistent data const [learningResponse, statusResponse, persistentResponse] = await Promise.all([ fetch('/api/automation/learning-status'), fetch('/api/automation/status'), @@ -72,22 +82,21 @@ const EnhancedAILearningPanel = () => { const statusData = await statusResponse.json(); const persistentData = await persistentResponse.json(); - // Merge persistent data with current learning status + // Merge current status with persistent data const safeData = { - learningSystem: { - ...learningData.learningSystem, - // Always include persistent statistics - persistentStats: persistentData.success ? persistentData.statistics : null, - recentTrades: persistentData.success ? persistentData.recentTrades : [], - systemHealth: persistentData.success ? persistentData.systemHealth : null + learningSystem: learningData.learningSystem || { + enabled: false, + message: learningData.message || 'Learning system not available', + activeDecisions: 0 }, visibility: learningData.visibility || { decisionTrackingActive: false, - learningDatabaseConnected: persistentData.success, + learningDatabaseConnected: false, aiEnhancementsActive: false, lastUpdateTime: new Date().toISOString() }, - automationStatus: statusData + automationStatus: statusData, + persistentData: persistentData.success ? persistentData.persistentData : null }; setLearningData(safeData); @@ -109,7 +118,8 @@ const EnhancedAILearningPanel = () => { aiEnhancementsActive: false, lastUpdateTime: new Date().toISOString() }, - automationStatus: null + automationStatus: null, + persistentData: null }); } finally { setLoading(false); @@ -300,6 +310,94 @@ const EnhancedAILearningPanel = () => { ); }; + const renderTradingStats = () => { + const stats = learningData?.persistentData?.tradingStats; + const enhanced = learningData?.persistentData?.enhancedSummary; + + if (!stats && !enhanced) { + return ( +