Add persistent learning data and PnL display

- Created persistent learning status API with trading statistics
- Added comprehensive PnL and win rate display to AI Learning panel
- Implemented trading stats tracking with win/loss ratios
- Added persistent data storage for historical trading performance
- Enhanced learning panel with real-time trading metrics
- Fixed learning data visibility when bot is not running
- Added sample trading data for demonstration
This commit is contained in:
mindesbunister
2025-07-27 13:57:52 +02:00
parent 7752463b9f
commit f623e46c26
4 changed files with 460 additions and 234 deletions

View File

@@ -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 (
<div className="bg-gray-800/30 rounded-lg p-4 border border-gray-600/30 mb-6">
<div className="text-gray-300 text-sm font-medium mb-2">📊 Trading Performance</div>
<div className="text-gray-400 text-sm">No trading data available yet</div>
</div>
);
}
return (
<div className="bg-gradient-to-br from-green-900/20 to-emerald-900/20 rounded-lg p-4 border border-green-500/30 mb-6">
<div className="text-green-300 text-sm font-medium mb-4 flex items-center justify-between">
<span>📊 Trading Performance</span>
{learningData?.persistentData?.isLive && (
<span className="text-xs bg-green-500/20 text-green-400 px-2 py-1 rounded-full">LIVE</span>
)}
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
<div className="text-center">
<div className="text-2xl font-bold text-green-400">
{stats?.totalTrades || enhanced?.totalTrades || 0}
</div>
<div className="text-green-300 text-xs">Total Trades</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-blue-400">
{stats?.winRate?.toFixed(1) || enhanced?.successRate?.toFixed(1) || '0.0'}%
</div>
<div className="text-blue-300 text-xs">Win Rate</div>
</div>
<div className="text-center">
<div className={`text-2xl font-bold ${(stats?.totalPnL || enhanced?.totalPnL || 0) >= 0 ? 'text-green-400' : 'text-red-400'}`}>
${(stats?.totalPnL || enhanced?.totalPnL || 0) >= 0 ? '+' : ''}{(stats?.totalPnL || enhanced?.totalPnL || 0).toFixed(2)}
</div>
<div className="text-gray-300 text-xs">Total PnL</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-purple-400">
{(enhanced?.systemConfidence || 0) * 100 || stats?.winRate || 0}%
</div>
<div className="text-purple-300 text-xs">AI Confidence</div>
</div>
</div>
{stats && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-gray-400">Winning Trades:</span>
<span className="text-green-400">{stats.winningTrades || 0}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-400">Losing Trades:</span>
<span className="text-red-400">{stats.losingTrades || 0}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-400">Avg Win:</span>
<span className="text-green-400">${(stats.avgWinAmount || 0).toFixed(2)}</span>
</div>
</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-gray-400">Avg Loss:</span>
<span className="text-red-400">${(stats.avgLossAmount || 0).toFixed(2)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-400">Best Trade:</span>
<span className="text-green-400">${(stats.bestTrade || 0).toFixed(2)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-400">Worst Trade:</span>
<span className="text-red-400">${(stats.worstTrade || 0).toFixed(2)}</span>
</div>
</div>
</div>
)}
</div>
);
};
return (
<div className="bg-gradient-to-br from-purple-900/20 to-blue-900/20 rounded-xl p-6 border border-purple-500/30">
<div className="flex items-center justify-between mb-6">
@@ -318,111 +416,7 @@ const EnhancedAILearningPanel = () => {
</button>
</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>
)}
{renderTradingStats()}
{renderLearningStatus()}
{visibility?.lastUpdateTime && (