Fix automation v2 runtime errors and ES module compatibility
- Fixed EnhancedAILearningPanel React error with recommendation objects - Converted automation-with-learning-v2.js to pure ES6 modules - Fixed singleton automation instance management - Resolved ES module/CommonJS compatibility issues - Added proper null safety checks for learning system data - Fixed API import paths for automation endpoints - Enhanced learning status display with proper error handling
This commit is contained in:
116
app/api/learning/persistent-status/route.js
Normal file
116
app/api/learning/persistent-status/route.js
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { TrendingUp } from 'lucide-react';
|
||||||
|
|
||||||
interface LearningData {
|
interface LearningData {
|
||||||
learningSystem: {
|
learningSystem: {
|
||||||
@@ -7,6 +9,23 @@ interface LearningData {
|
|||||||
activeDecisions?: number;
|
activeDecisions?: number;
|
||||||
message?: string;
|
message?: string;
|
||||||
recommendation?: 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?: {
|
report?: {
|
||||||
summary?: {
|
summary?: {
|
||||||
totalDecisions?: number;
|
totalDecisions?: number;
|
||||||
@@ -42,25 +61,29 @@ const EnhancedAILearningPanel = () => {
|
|||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// Get both learning status and automation status
|
// Get both learning status and persistent data (regardless of automation status)
|
||||||
const [learningResponse, statusResponse] = await Promise.all([
|
const [learningResponse, statusResponse, persistentResponse] = await Promise.all([
|
||||||
fetch('/api/automation/learning-status'),
|
fetch('/api/automation/learning-status'),
|
||||||
fetch('/api/automation/status')
|
fetch('/api/automation/status'),
|
||||||
|
fetch('/api/learning/persistent-status')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const learningData = await learningResponse.json();
|
const learningData = await learningResponse.json();
|
||||||
const statusData = await statusResponse.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 = {
|
const safeData = {
|
||||||
learningSystem: learningData.learningSystem || {
|
learningSystem: {
|
||||||
enabled: false,
|
...learningData.learningSystem,
|
||||||
message: learningData.message || 'Learning system not available',
|
// Always include persistent statistics
|
||||||
activeDecisions: 0
|
persistentStats: persistentData.success ? persistentData.statistics : null,
|
||||||
|
recentTrades: persistentData.success ? persistentData.recentTrades : [],
|
||||||
|
systemHealth: persistentData.success ? persistentData.systemHealth : null
|
||||||
},
|
},
|
||||||
visibility: learningData.visibility || {
|
visibility: learningData.visibility || {
|
||||||
decisionTrackingActive: false,
|
decisionTrackingActive: false,
|
||||||
learningDatabaseConnected: false,
|
learningDatabaseConnected: persistentData.success,
|
||||||
aiEnhancementsActive: false,
|
aiEnhancementsActive: false,
|
||||||
lastUpdateTime: new Date().toISOString()
|
lastUpdateTime: new Date().toISOString()
|
||||||
},
|
},
|
||||||
@@ -295,6 +318,111 @@ const EnhancedAILearningPanel = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Trading Performance Section - Always show if we have persistent data */}
|
||||||
|
{learningData?.learningSystem?.persistentStats && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.3 }}
|
||||||
|
className="bg-gradient-to-br from-blue-900/30 to-purple-900/30 rounded-xl p-6 border border-blue-500/30 mb-6"
|
||||||
|
>
|
||||||
|
<h4 className="text-xl font-bold text-blue-300 mb-4 flex items-center gap-2">
|
||||||
|
<TrendingUp className="w-5 h-5" />
|
||||||
|
Trading Performance
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||||
|
<div className="bg-black/30 rounded-lg p-4 text-center">
|
||||||
|
<div className="text-2xl font-bold text-green-400">
|
||||||
|
{learningData.learningSystem.persistentStats.totalTrades}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-400">Total Trades</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-black/30 rounded-lg p-4 text-center">
|
||||||
|
<div className="text-2xl font-bold text-blue-400">
|
||||||
|
{learningData.learningSystem.persistentStats.successRate?.toFixed(1)}%
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-400">Success Rate</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-black/30 rounded-lg p-4 text-center">
|
||||||
|
<div className={`text-2xl font-bold ${
|
||||||
|
learningData.learningSystem.persistentStats.totalPnl >= 0 ? 'text-green-400' : 'text-red-400'
|
||||||
|
}`}>
|
||||||
|
${learningData.learningSystem.persistentStats.totalPnl?.toFixed(2)}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-400">Total P&L</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-black/30 rounded-lg p-4 text-center">
|
||||||
|
<div className="text-2xl font-bold text-yellow-400">
|
||||||
|
{learningData.learningSystem.persistentStats.winRate?.toFixed(0)}%
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-400">Win Rate</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Recent Trades */}
|
||||||
|
{learningData.learningSystem.recentTrades && learningData.learningSystem.recentTrades.length > 0 && (
|
||||||
|
<div className="mt-6">
|
||||||
|
<h5 className="text-lg font-semibold text-blue-300 mb-3">Recent Trades</h5>
|
||||||
|
<div className="space-y-2 max-h-64 overflow-y-auto">
|
||||||
|
{learningData.learningSystem.recentTrades.map((trade: any, index: number) => (
|
||||||
|
<div key={index} className="bg-black/20 rounded-lg p-3 flex justify-between items-center">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-sm font-medium text-gray-300">{trade.symbol}</span>
|
||||||
|
<span className={`text-xs px-2 py-1 rounded ${
|
||||||
|
trade.type === 'long' ? 'bg-green-900/50 text-green-300' : 'bg-red-900/50 text-red-300'
|
||||||
|
}`}>
|
||||||
|
{trade.type?.toUpperCase()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<div className={`text-sm font-semibold ${
|
||||||
|
parseFloat(trade.pnl) >= 0 ? 'text-green-400' : 'text-red-400'
|
||||||
|
}`}>
|
||||||
|
${parseFloat(trade.pnl).toFixed(2)}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-gray-500">
|
||||||
|
{new Date(trade.updatedAt).toLocaleDateString()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* System Health */}
|
||||||
|
{learningData.learningSystem.systemHealth && (
|
||||||
|
<div className="mt-6 p-4 bg-black/20 rounded-lg">
|
||||||
|
<h5 className="text-lg font-semibold text-blue-300 mb-2">System Health</h5>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-400">AI Decisions:</span>
|
||||||
|
<span className="ml-2 text-white font-medium">
|
||||||
|
{learningData.learningSystem.systemHealth.totalDecisions?.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-400">Recent Activity:</span>
|
||||||
|
<span className="ml-2 text-white font-medium">
|
||||||
|
{learningData.learningSystem.systemHealth.recentDecisions} decisions
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-400">Last Updated:</span>
|
||||||
|
<span className="ml-2 text-white font-medium">
|
||||||
|
{new Date(learningData.learningSystem.systemHealth.lastActivity).toLocaleTimeString()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
|
||||||
{renderLearningStatus()}
|
{renderLearningStatus()}
|
||||||
|
|
||||||
{visibility?.lastUpdateTime && (
|
{visibility?.lastUpdateTime && (
|
||||||
|
|||||||
53
package-lock.json
generated
53
package-lock.json
generated
@@ -15,7 +15,9 @@
|
|||||||
"@zetamarkets/sdk": "^1.61.0",
|
"@zetamarkets/sdk": "^1.61.0",
|
||||||
"bs58": "^6.0.0",
|
"bs58": "^6.0.0",
|
||||||
"dotenv": "^17.2.0",
|
"dotenv": "^17.2.0",
|
||||||
|
"framer-motion": "^12.23.9",
|
||||||
"lightweight-charts": "^4.1.3",
|
"lightweight-charts": "^4.1.3",
|
||||||
|
"lucide-react": "^0.526.0",
|
||||||
"next": "15.3.5",
|
"next": "15.3.5",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"openai": "^5.8.3",
|
"openai": "^5.8.3",
|
||||||
@@ -7835,6 +7837,33 @@
|
|||||||
"url": "https://github.com/sponsors/rawify"
|
"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": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@@ -10645,6 +10674,15 @@
|
|||||||
"node": ">=12"
|
"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": {
|
"node_modules/make-dir": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
@@ -10913,6 +10951,21 @@
|
|||||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/mri": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||||
|
|||||||
@@ -49,7 +49,9 @@
|
|||||||
"@zetamarkets/sdk": "^1.61.0",
|
"@zetamarkets/sdk": "^1.61.0",
|
||||||
"bs58": "^6.0.0",
|
"bs58": "^6.0.0",
|
||||||
"dotenv": "^17.2.0",
|
"dotenv": "^17.2.0",
|
||||||
|
"framer-motion": "^12.23.9",
|
||||||
"lightweight-charts": "^4.1.3",
|
"lightweight-charts": "^4.1.3",
|
||||||
|
"lucide-react": "^0.526.0",
|
||||||
"next": "15.3.5",
|
"next": "15.3.5",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"openai": "^5.8.3",
|
"openai": "^5.8.3",
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user