diff --git a/app/api/learning/persistent-status/route.js b/app/api/learning/persistent-status/route.js new file mode 100644 index 0000000..cb3b04b --- /dev/null +++ b/app/api/learning/persistent-status/route.js @@ -0,0 +1,116 @@ +// API route for persistent learning data that works regardless of automation status +import { NextResponse } from 'next/server'; +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +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; + + // 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 + } + } + }; + + return NextResponse.json(persistentData); + + } catch (error) { + console.error('❌ Error fetching persistent learning data:', 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 + } + }, { status: 500 }); + } finally { + await prisma.$disconnect(); + } +} diff --git a/components/EnhancedAILearningPanel.tsx b/components/EnhancedAILearningPanel.tsx index ff03cdd..8ed146c 100644 --- a/components/EnhancedAILearningPanel.tsx +++ b/components/EnhancedAILearningPanel.tsx @@ -1,4 +1,6 @@ import React, { useState, useEffect } from 'react'; +import { motion } from 'framer-motion'; +import { TrendingUp } from 'lucide-react'; interface LearningData { learningSystem: { @@ -7,6 +9,23 @@ 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; @@ -42,25 +61,29 @@ const EnhancedAILearningPanel = () => { try { setLoading(true); - // Get both learning status and automation status - const [learningResponse, statusResponse] = await Promise.all([ + // Get both learning status and persistent data (regardless of automation status) + const [learningResponse, statusResponse, persistentResponse] = await Promise.all([ fetch('/api/automation/learning-status'), - fetch('/api/automation/status') + fetch('/api/automation/status'), + fetch('/api/learning/persistent-status') ]); const learningData = await learningResponse.json(); const statusData = await statusResponse.json(); + const persistentData = await persistentResponse.json(); - // Ensure we have a proper data structure even if APIs return errors + // Merge persistent data with current learning status const safeData = { - learningSystem: learningData.learningSystem || { - enabled: false, - message: learningData.message || 'Learning system not available', - activeDecisions: 0 + learningSystem: { + ...learningData.learningSystem, + // Always include persistent statistics + persistentStats: persistentData.success ? persistentData.statistics : null, + recentTrades: persistentData.success ? persistentData.recentTrades : [], + systemHealth: persistentData.success ? persistentData.systemHealth : null }, visibility: learningData.visibility || { decisionTrackingActive: false, - learningDatabaseConnected: false, + learningDatabaseConnected: persistentData.success, aiEnhancementsActive: false, lastUpdateTime: new Date().toISOString() }, @@ -295,6 +318,111 @@ const EnhancedAILearningPanel = () => { + {/* Trading Performance Section - Always show if we have persistent data */} + {learningData?.learningSystem?.persistentStats && ( + +

+ + Trading Performance +

+ +
+
+
+ {learningData.learningSystem.persistentStats.totalTrades} +
+
Total Trades
+
+ +
+
+ {learningData.learningSystem.persistentStats.successRate?.toFixed(1)}% +
+
Success Rate
+
+ +
+
= 0 ? 'text-green-400' : 'text-red-400' + }`}> + ${learningData.learningSystem.persistentStats.totalPnl?.toFixed(2)} +
+
Total P&L
+
+ +
+
+ {learningData.learningSystem.persistentStats.winRate?.toFixed(0)}% +
+
Win Rate
+
+
+ + {/* Recent Trades */} + {learningData.learningSystem.recentTrades && learningData.learningSystem.recentTrades.length > 0 && ( +
+
Recent Trades
+
+ {learningData.learningSystem.recentTrades.map((trade: any, index: number) => ( +
+
+ {trade.symbol} + + {trade.type?.toUpperCase()} + +
+
+
= 0 ? 'text-green-400' : 'text-red-400' + }`}> + ${parseFloat(trade.pnl).toFixed(2)} +
+
+ {new Date(trade.updatedAt).toLocaleDateString()} +
+
+
+ ))} +
+
+ )} + + {/* System Health */} + {learningData.learningSystem.systemHealth && ( +
+
System Health
+
+
+ AI Decisions: + + {learningData.learningSystem.systemHealth.totalDecisions?.toLocaleString()} + +
+
+ Recent Activity: + + {learningData.learningSystem.systemHealth.recentDecisions} decisions + +
+
+ Last Updated: + + {new Date(learningData.learningSystem.systemHealth.lastActivity).toLocaleTimeString()} + +
+
+
+ )} +
+ )} + {renderLearningStatus()} {visibility?.lastUpdateTime && ( diff --git a/package-lock.json b/package-lock.json index 5fd0aaf..5dcf7c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,9 @@ "@zetamarkets/sdk": "^1.61.0", "bs58": "^6.0.0", "dotenv": "^17.2.0", + "framer-motion": "^12.23.9", "lightweight-charts": "^4.1.3", + "lucide-react": "^0.526.0", "next": "15.3.5", "node-fetch": "^3.3.2", "openai": "^5.8.3", @@ -7835,6 +7837,33 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "12.23.9", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.9.tgz", + "integrity": "sha512-TqEHXj8LWfQSKqfdr5Y4mYltYLw96deu6/K9kGDd+ysqRJPNwF9nb5mZcrLmybHbU7gcJ+HQar41U3UTGanbbQ==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.9", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -10645,6 +10674,15 @@ "node": ">=12" } }, + "node_modules/lucide-react": { + "version": "0.526.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.526.0.tgz", + "integrity": "sha512-uGWG/2RKuDLeQHCodn5cmJ9Zij80EstOdcBP+j//B2sr78w7woiEL4aMu6CRlRkyOyJ8sZry8QLhQTmZjynLdA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -10913,6 +10951,21 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "license": "MIT" }, + "node_modules/motion-dom": { + "version": "12.23.9", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.9.tgz", + "integrity": "sha512-6Sv++iWS8XMFCgU1qwKj9l4xuC47Hp4+2jvPfyTXkqDg2tTzSgX6nWKD4kNFXk0k7llO59LZTPuJigza4A2K1A==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", diff --git a/package.json b/package.json index cb9db3a..5229a3b 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,9 @@ "@zetamarkets/sdk": "^1.61.0", "bs58": "^6.0.0", "dotenv": "^17.2.0", + "framer-motion": "^12.23.9", "lightweight-charts": "^4.1.3", + "lucide-react": "^0.526.0", "next": "15.3.5", "node-fetch": "^3.3.2", "openai": "^5.8.3", diff --git a/prisma/prisma/dev.db b/prisma/prisma/dev.db index 7a43a44..a3357d7 100644 Binary files a/prisma/prisma/dev.db and b/prisma/prisma/dev.db differ