UI: Enhance AI learning dashboard visual design

Improvements:
- Add gradient background with glowing effects
- Redesign header section with larger icon and enhanced typography
- Improve card layouts with modern shadows and hover effects
- Optimize Sharpe ratio and profit factor display sections
- Add better visual hierarchy and spacing
- Enhance color schemes for better readability
- Remove unused decision record fetching code
- Clean up comments for better code clarity

Visual enhancements:
- Glassmorphism effects with backdrop blur
- Animated gradient backgrounds
- Enhanced metric cards with better contrast
- Improved symbol performance table design
- Modernized trade history cards with gradients

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tinkle
2025-10-29 04:54:53 +08:00
parent 102d9f438d
commit 390c8fd588

View File

@@ -33,7 +33,7 @@ interface PerformanceAnalysis {
avg_win: number; avg_win: number;
avg_loss: number; avg_loss: number;
profit_factor: number; profit_factor: number;
sharpe_ratio: number; // 夏普比率(风险调整后收益) sharpe_ratio: number;
recent_trades: TradeOutcome[]; recent_trades: TradeOutcome[];
symbol_stats: { [key: string]: SymbolPerformance }; symbol_stats: { [key: string]: SymbolPerformance };
best_symbol: string; best_symbol: string;
@@ -44,14 +44,6 @@ interface AILearningProps {
traderId: string; traderId: string;
} }
interface DecisionRecord {
timestamp: string;
cycle_number: number;
input_prompt: string;
cot_trace: string;
success: boolean;
}
const fetcher = (url: string) => fetch(url).then(res => res.json()); const fetcher = (url: string) => fetch(url).then(res => res.json());
export default function AILearning({ traderId }: AILearningProps) { export default function AILearning({ traderId }: AILearningProps) {
@@ -62,13 +54,6 @@ export default function AILearning({ traderId }: AILearningProps) {
{ refreshInterval: 10000 } { refreshInterval: 10000 }
); );
// 获取最新的决策记录查看AI的思考过程
const { data: latestDecisions } = useSWR<DecisionRecord[]>(
`http://localhost:8080/api/decisions/latest?trader_id=${traderId}`,
fetcher,
{ refreshInterval: 10000 }
);
if (error) { if (error) {
return ( return (
<div className="rounded p-6" style={{ background: '#1E2329', border: '1px solid #2B3139' }}> <div className="rounded p-6" style={{ background: '#1E2329', border: '1px solid #2B3139' }}>
@@ -99,128 +84,185 @@ export default function AILearning({ traderId }: AILearningProps) {
); );
} }
// 安全地获取symbol_stats
const symbolStats = performance.symbol_stats || {}; const symbolStats = performance.symbol_stats || {};
const symbolStatsList = Object.values(symbolStats).filter(stat => stat != null).sort( const symbolStatsList = Object.values(symbolStats).filter(stat => stat != null).sort(
(a, b) => (b.total_pn_l || 0) - (a.total_pn_l || 0) (a, b) => (b.total_pn_l || 0) - (a.total_pn_l || 0)
); );
return ( return (
<div className="space-y-6"> <div className="space-y-8">
{/* 标题区 - 更简洁 */} {/* 标题区 - 优化设计 */}
<div className="flex items-center gap-4"> <div className="relative rounded-2xl p-6 overflow-hidden" style={{
<div className="w-12 h-12 rounded-xl flex items-center justify-center text-2xl" style={{ background: 'linear-gradient(135deg, rgba(139, 92, 246, 0.15) 0%, rgba(99, 102, 241, 0.1) 50%, rgba(30, 35, 41, 0.8) 100%)',
border: '1px solid rgba(139, 92, 246, 0.3)',
boxShadow: '0 8px 32px rgba(139, 92, 246, 0.2)'
}}>
<div className="absolute top-0 right-0 w-96 h-96 rounded-full opacity-10" style={{
background: 'radial-gradient(circle, #8B5CF6 0%, transparent 70%)',
filter: 'blur(60px)'
}} />
<div className="relative flex items-center gap-4">
<div className="w-16 h-16 rounded-2xl flex items-center justify-center text-3xl" style={{
background: 'linear-gradient(135deg, #8B5CF6 0%, #6366F1 100%)', background: 'linear-gradient(135deg, #8B5CF6 0%, #6366F1 100%)',
boxShadow: '0 4px 14px rgba(139, 92, 246, 0.4)' boxShadow: '0 8px 24px rgba(139, 92, 246, 0.5)',
border: '2px solid rgba(255, 255, 255, 0.1)'
}}> }}>
🧠 🧠
</div> </div>
<div> <div>
<h2 className="text-2xl font-bold" style={{ color: '#EAECEF' }}>{t('aiLearning', language)}</h2> <h2 className="text-3xl font-bold mb-1" style={{
<p className="text-sm" style={{ color: '#848E9C' }}> color: '#EAECEF',
textShadow: '0 2px 8px rgba(139, 92, 246, 0.3)'
}}>
{t('aiLearning', language)}
</h2>
<p className="text-base" style={{ color: '#A78BFA' }}>
{t('tradesAnalyzed', language, { count: performance.total_trades })} {t('tradesAnalyzed', language, { count: performance.total_trades })}
</p> </p>
</div> </div>
</div> </div>
</div>
{/* 主要内容:现代化网格布局 */} {/* 核心指标卡片 - 4列网格 */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{/* 左侧大区域:核心指标 (5列) */}
<div className="lg:col-span-5 space-y-6">
{/* 核心指标网格 - 玻璃态设计 */}
<div className="grid grid-cols-2 gap-4">
{/* 总交易数 */} {/* 总交易数 */}
<div className="rounded-xl p-4 backdrop-blur-sm" style={{ <div className="rounded-2xl p-5 relative overflow-hidden group hover:scale-105 transition-transform" style={{
background: 'rgba(30, 35, 41, 0.6)', background: 'linear-gradient(135deg, rgba(99, 102, 241, 0.2) 0%, rgba(30, 35, 41, 0.8) 100%)',
border: '1px solid rgba(99, 102, 241, 0.2)', border: '1px solid rgba(99, 102, 241, 0.3)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)' boxShadow: '0 4px 16px rgba(99, 102, 241, 0.2)'
}}> }}>
<div className="text-xs font-semibold mb-2" style={{ color: '#94A3B8' }}>{t('totalTrades', language)}</div> <div className="absolute top-0 right-0 w-24 h-24 rounded-full opacity-20" style={{
<div className="text-3xl font-bold mono" style={{ color: '#E0E7FF' }}> background: 'radial-gradient(circle, #6366F1 0%, transparent 70%)',
filter: 'blur(20px)'
}} />
<div className="relative">
<div className="text-xs font-semibold mb-3 uppercase tracking-wider" style={{ color: '#A5B4FC' }}>
{t('totalTrades', language)}
</div>
<div className="text-4xl font-bold mono mb-1" style={{ color: '#E0E7FF' }}>
{performance.total_trades} {performance.total_trades}
</div> </div>
<div className="text-xs" style={{ color: '#6366F1' }}>📊 Trades</div>
</div>
</div> </div>
{/* 胜率 */} {/* 胜率 */}
<div className="rounded-xl p-4 backdrop-blur-sm" style={{ <div className="rounded-2xl p-5 relative overflow-hidden group hover:scale-105 transition-transform" style={{
background: (performance.win_rate || 0) >= 50 background: (performance.win_rate || 0) >= 50
? 'rgba(14, 203, 129, 0.1)' ? 'linear-gradient(135deg, rgba(16, 185, 129, 0.2) 0%, rgba(30, 35, 41, 0.8) 100%)'
: 'rgba(246, 70, 93, 0.1)', : 'linear-gradient(135deg, rgba(248, 113, 113, 0.2) 0%, rgba(30, 35, 41, 0.8) 100%)',
border: `1px solid ${(performance.win_rate || 0) >= 50 ? 'rgba(14, 203, 129, 0.3)' : 'rgba(246, 70, 93, 0.3)'}`, border: `1px solid ${(performance.win_rate || 0) >= 50 ? 'rgba(16, 185, 129, 0.4)' : 'rgba(248, 113, 113, 0.4)'}`,
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)' boxShadow: `0 4px 16px ${(performance.win_rate || 0) >= 50 ? 'rgba(16, 185, 129, 0.2)' : 'rgba(248, 113, 113, 0.2)'}`
}}> }}>
<div className="text-xs font-semibold mb-2" style={{ color: '#94A3B8' }}>{t('winRate', language)}</div> <div className="absolute top-0 right-0 w-24 h-24 rounded-full opacity-20" style={{
<div className="text-3xl font-bold mono" style={{ background: `radial-gradient(circle, ${(performance.win_rate || 0) >= 50 ? '#10B981' : '#F87171'} 0%, transparent 70%)`,
filter: 'blur(20px)'
}} />
<div className="relative">
<div className="text-xs font-semibold mb-3 uppercase tracking-wider" style={{
color: (performance.win_rate || 0) >= 50 ? '#6EE7B7' : '#FCA5A5'
}}>
{t('winRate', language)}
</div>
<div className="text-4xl font-bold mono mb-1" style={{
color: (performance.win_rate || 0) >= 50 ? '#10B981' : '#F87171' color: (performance.win_rate || 0) >= 50 ? '#10B981' : '#F87171'
}}> }}>
{(performance.win_rate || 0).toFixed(1)}% {(performance.win_rate || 0).toFixed(1)}%
</div> </div>
<div className="text-xs mt-1" style={{ color: '#94A3B8' }}> <div className="text-xs" style={{ color: '#94A3B8' }}>
{performance.winning_trades || 0}W / {performance.losing_trades || 0}L {performance.winning_trades || 0}W / {performance.losing_trades || 0}L
</div> </div>
</div> </div>
</div>
{/* 平均盈利 */} {/* 平均盈利 */}
<div className="rounded-xl p-4 backdrop-blur-sm" style={{ <div className="rounded-2xl p-5 relative overflow-hidden group hover:scale-105 transition-transform" style={{
background: 'rgba(14, 203, 129, 0.08)', background: 'linear-gradient(135deg, rgba(14, 203, 129, 0.2) 0%, rgba(30, 35, 41, 0.8) 100%)',
border: '1px solid rgba(14, 203, 129, 0.2)', border: '1px solid rgba(14, 203, 129, 0.3)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)' boxShadow: '0 4px 16px rgba(14, 203, 129, 0.2)'
}}> }}>
<div className="text-xs font-semibold mb-2" style={{ color: '#94A3B8' }}>{t('avgWin', language)}</div> <div className="absolute top-0 right-0 w-24 h-24 rounded-full opacity-20" style={{
<div className="text-3xl font-bold mono" style={{ color: '#10B981' }}> background: 'radial-gradient(circle, #0ECB81 0%, transparent 70%)',
filter: 'blur(20px)'
}} />
<div className="relative">
<div className="text-xs font-semibold mb-3 uppercase tracking-wider" style={{ color: '#6EE7B7' }}>
{t('avgWin', language)}
</div>
<div className="text-4xl font-bold mono mb-1" style={{ color: '#10B981' }}>
+{(performance.avg_win || 0).toFixed(2)}% +{(performance.avg_win || 0).toFixed(2)}%
</div> </div>
<div className="text-xs" style={{ color: '#6EE7B7' }}>📈 Average</div>
</div>
</div> </div>
{/* 平均亏损 */} {/* 平均亏损 */}
<div className="rounded-xl p-4 backdrop-blur-sm" style={{ <div className="rounded-2xl p-5 relative overflow-hidden group hover:scale-105 transition-transform" style={{
background: 'rgba(246, 70, 93, 0.08)', background: 'linear-gradient(135deg, rgba(246, 70, 93, 0.2) 0%, rgba(30, 35, 41, 0.8) 100%)',
border: '1px solid rgba(246, 70, 93, 0.2)', border: '1px solid rgba(246, 70, 93, 0.3)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)' boxShadow: '0 4px 16px rgba(246, 70, 93, 0.2)'
}}> }}>
<div className="text-xs font-semibold mb-2" style={{ color: '#94A3B8' }}>{t('avgLoss', language)}</div> <div className="absolute top-0 right-0 w-24 h-24 rounded-full opacity-20" style={{
<div className="text-3xl font-bold mono" style={{ color: '#F87171' }}> background: 'radial-gradient(circle, #F6465D 0%, transparent 70%)',
filter: 'blur(20px)'
}} />
<div className="relative">
<div className="text-xs font-semibold mb-3 uppercase tracking-wider" style={{ color: '#FCA5A5' }}>
{t('avgLoss', language)}
</div>
<div className="text-4xl font-bold mono mb-1" style={{ color: '#F87171' }}>
{(performance.avg_loss || 0).toFixed(2)}% {(performance.avg_loss || 0).toFixed(2)}%
</div> </div>
<div className="text-xs" style={{ color: '#FCA5A5' }}>📉 Average</div>
</div>
</div> </div>
</div> </div>
{/* 夏普比率 - AI自我进化核心指标 */} {/* 关键指标:夏普比率 & 盈亏比 - 2列网格 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* 夏普比率 */}
<div className="rounded-2xl p-6 relative overflow-hidden" style={{ <div className="rounded-2xl p-6 relative overflow-hidden" style={{
background: 'linear-gradient(135deg, rgba(139, 92, 246, 0.2) 0%, rgba(99, 102, 241, 0.1) 100%)', background: 'linear-gradient(135deg, rgba(139, 92, 246, 0.25) 0%, rgba(99, 102, 241, 0.15) 50%, rgba(30, 35, 41, 0.9) 100%)',
border: '2px solid rgba(139, 92, 246, 0.4)', border: '2px solid rgba(139, 92, 246, 0.5)',
boxShadow: '0 10px 30px rgba(139, 92, 246, 0.3)' boxShadow: '0 12px 40px rgba(139, 92, 246, 0.3)'
}}> }}>
<div className="absolute top-0 right-0 w-40 h-40 rounded-full opacity-20" style={{ <div className="absolute top-0 right-0 w-48 h-48 rounded-full opacity-20" style={{
background: 'radial-gradient(circle, #8B5CF6 0%, transparent 70%)', background: 'radial-gradient(circle, #8B5CF6 0%, transparent 70%)',
filter: 'blur(30px)' filter: 'blur(40px)'
}} /> }} />
<div className="relative"> <div className="relative">
<div className="flex items-center gap-2 mb-3"> <div className="flex items-center gap-3 mb-4">
<span className="text-2xl">🧬</span> <div className="w-12 h-12 rounded-xl flex items-center justify-center text-2xl" style={{
background: 'rgba(139, 92, 246, 0.3)',
border: '1px solid rgba(139, 92, 246, 0.5)'
}}>
🧬
</div>
<div> <div>
<div className="text-sm font-bold" style={{ color: '#C4B5FD' }}></div> <div className="text-lg font-bold" style={{ color: '#C4B5FD' }}></div>
<div className="text-xs" style={{ color: '#94A3B8' }}> · AI自我进化指标</div> <div className="text-xs" style={{ color: '#94A3B8' }}> · AI自我进化指标</div>
</div> </div>
</div> </div>
<div className="flex items-end justify-between"> <div className="flex items-end justify-between mb-4">
<div className="text-6xl font-bold mono" style={{ <div className="text-6xl font-bold mono" style={{
color: (performance.sharpe_ratio || 0) >= 2 ? '#10B981' : color: (performance.sharpe_ratio || 0) >= 2 ? '#10B981' :
(performance.sharpe_ratio || 0) >= 1 ? '#22D3EE' : (performance.sharpe_ratio || 0) >= 1 ? '#22D3EE' :
(performance.sharpe_ratio || 0) >= 0 ? '#F0B90B' : '#F87171' (performance.sharpe_ratio || 0) >= 0 ? '#F0B90B' : '#F87171',
textShadow: '0 4px 12px rgba(0, 0, 0, 0.3)'
}}> }}>
{performance.sharpe_ratio ? performance.sharpe_ratio.toFixed(2) : 'N/A'} {performance.sharpe_ratio ? performance.sharpe_ratio.toFixed(2) : 'N/A'}
</div> </div>
{performance.sharpe_ratio !== undefined && ( {performance.sharpe_ratio !== undefined && (
<div className="text-right mb-2"> <div className="text-right mb-2">
<div className="text-xs font-bold mb-1" style={{ <div className="text-sm font-bold px-3 py-1 rounded-lg" style={{
color: (performance.sharpe_ratio || 0) >= 2 ? '#10B981' : color: (performance.sharpe_ratio || 0) >= 2 ? '#10B981' :
(performance.sharpe_ratio || 0) >= 1 ? '#22D3EE' : (performance.sharpe_ratio || 0) >= 1 ? '#22D3EE' :
(performance.sharpe_ratio || 0) >= 0 ? '#F0B90B' : '#F87171' (performance.sharpe_ratio || 0) >= 0 ? '#F0B90B' : '#F87171',
background: (performance.sharpe_ratio || 0) >= 2 ? 'rgba(16, 185, 129, 0.2)' :
(performance.sharpe_ratio || 0) >= 1 ? 'rgba(34, 211, 238, 0.2)' :
(performance.sharpe_ratio || 0) >= 0 ? 'rgba(240, 185, 11, 0.2)' : 'rgba(248, 113, 113, 0.2)'
}}> }}>
{performance.sharpe_ratio >= 2 ? '🟢 卓越表现' : {performance.sharpe_ratio >= 2 ? '🟢 卓越表现' :
performance.sharpe_ratio >= 1 ? '🟢 良好表现' : performance.sharpe_ratio >= 1 ? '🟢 良好表现' :
@@ -231,11 +273,11 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
{performance.sharpe_ratio !== undefined && ( {performance.sharpe_ratio !== undefined && (
<div className="mt-4 p-3 rounded-xl" style={{ <div className="rounded-xl p-4" style={{
background: 'rgba(0, 0, 0, 0.3)', background: 'rgba(0, 0, 0, 0.4)',
border: '1px solid rgba(139, 92, 246, 0.2)' border: '1px solid rgba(139, 92, 246, 0.3)'
}}> }}>
<div className="text-xs leading-relaxed" style={{ color: '#C4B5FD' }}> <div className="text-sm leading-relaxed" style={{ color: '#DDD6FE' }}>
{performance.sharpe_ratio >= 2 && '✨ AI策略非常有效风险调整后收益优异可适度扩大仓位但保持纪律。'} {performance.sharpe_ratio >= 2 && '✨ AI策略非常有效风险调整后收益优异可适度扩大仓位但保持纪律。'}
{performance.sharpe_ratio >= 1 && performance.sharpe_ratio < 2 && '✅ 策略表现稳健,风险收益平衡良好,继续保持当前策略。'} {performance.sharpe_ratio >= 1 && performance.sharpe_ratio < 2 && '✅ 策略表现稳健,风险收益平衡良好,继续保持当前策略。'}
{performance.sharpe_ratio >= 0 && performance.sharpe_ratio < 1 && '⚠️ 收益为正但波动较大AI正在优化策略降低风险。'} {performance.sharpe_ratio >= 0 && performance.sharpe_ratio < 1 && '⚠️ 收益为正但波动较大AI正在优化策略降低风险。'}
@@ -246,36 +288,50 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
</div> </div>
{/* 盈亏比 - 突出显示 */} {/* 盈亏比 */}
<div className="rounded-2xl p-6 relative overflow-hidden" style={{ <div className="rounded-2xl p-6 relative overflow-hidden" style={{
background: 'linear-gradient(135deg, rgba(240, 185, 11, 0.15) 0%, rgba(252, 213, 53, 0.05) 100%)', background: 'linear-gradient(135deg, rgba(240, 185, 11, 0.25) 0%, rgba(252, 213, 53, 0.15) 50%, rgba(30, 35, 41, 0.9) 100%)',
border: '1px solid rgba(240, 185, 11, 0.3)', border: '2px solid rgba(240, 185, 11, 0.5)',
boxShadow: '0 8px 24px rgba(240, 185, 11, 0.2)' boxShadow: '0 12px 40px rgba(240, 185, 11, 0.3)'
}}> }}>
<div className="absolute top-0 right-0 w-40 h-40 rounded-full opacity-20" style={{ <div className="absolute top-0 right-0 w-48 h-48 rounded-full opacity-20" style={{
background: 'radial-gradient(circle, #F0B90B 0%, transparent 70%)', background: 'radial-gradient(circle, #F0B90B 0%, transparent 70%)',
filter: 'blur(30px)' filter: 'blur(40px)'
}} /> }} />
<div className="relative">
<div className="relative flex items-center justify-between"> <div className="flex items-center gap-3 mb-4">
<div className="w-12 h-12 rounded-xl flex items-center justify-center text-2xl" style={{
background: 'rgba(240, 185, 11, 0.3)',
border: '1px solid rgba(240, 185, 11, 0.5)'
}}>
💰
</div>
<div> <div>
<div className="text-sm font-semibold mb-1" style={{ color: '#FCD34D' }}>{t('profitFactor', language)}</div> <div className="text-lg font-bold" style={{ color: '#FCD34D' }}>
{t('profitFactor', language)}
</div>
<div className="text-xs" style={{ color: '#94A3B8' }}> <div className="text-xs" style={{ color: '#94A3B8' }}>
{t('avgWinDivLoss', language)} {t('avgWinDivLoss', language)}
</div> </div>
</div> </div>
<div className="text-5xl font-bold mono" style={{ </div>
<div className="flex items-end justify-between mb-4">
<div className="text-6xl font-bold mono" style={{
color: (performance.profit_factor || 0) >= 2.0 ? '#10B981' : color: (performance.profit_factor || 0) >= 2.0 ? '#10B981' :
(performance.profit_factor || 0) >= 1.5 ? '#F0B90B' : (performance.profit_factor || 0) >= 1.5 ? '#F0B90B' :
(performance.profit_factor || 0) >= 1.0 ? '#FB923C' : '#F87171' (performance.profit_factor || 0) >= 1.0 ? '#FB923C' : '#F87171',
textShadow: '0 4px 12px rgba(0, 0, 0, 0.3)'
}}> }}>
{(performance.profit_factor || 0) > 0 ? (performance.profit_factor || 0).toFixed(2) : 'N/A'} {(performance.profit_factor || 0) > 0 ? (performance.profit_factor || 0).toFixed(2) : 'N/A'}
</div> </div>
</div>
<div className="mt-3 text-xs font-semibold" style={{ <div className="text-right mb-2">
<div className="text-sm font-bold px-3 py-1 rounded-lg" style={{
color: (performance.profit_factor || 0) >= 2.0 ? '#10B981' : color: (performance.profit_factor || 0) >= 2.0 ? '#10B981' :
(performance.profit_factor || 0) >= 1.5 ? '#F0B90B' : '#94A3B8' (performance.profit_factor || 0) >= 1.5 ? '#F0B90B' : '#94A3B8',
background: (performance.profit_factor || 0) >= 2.0 ? 'rgba(16, 185, 129, 0.2)' :
(performance.profit_factor || 0) >= 1.5 ? 'rgba(240, 185, 11, 0.2)' : 'rgba(148, 163, 184, 0.2)'
}}> }}>
{(performance.profit_factor || 0) >= 2.0 && t('excellent', language)} {(performance.profit_factor || 0) >= 2.0 && t('excellent', language)}
{(performance.profit_factor || 0) >= 1.5 && (performance.profit_factor || 0) < 2.0 && t('good', language)} {(performance.profit_factor || 0) >= 1.5 && (performance.profit_factor || 0) < 2.0 && t('good', language)}
@@ -284,28 +340,40 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
</div> </div>
</div> </div>
{/* 左侧结束 */}
{/* 中间列:数据表格 (4列) */} <div className="rounded-xl p-4" style={{
<div className="lg:col-span-4 space-y-6"> background: 'rgba(0, 0, 0, 0.4)',
{/* 最佳/最差币种 */} border: '1px solid rgba(240, 185, 11, 0.3)'
}}>
<div className="text-sm leading-relaxed" style={{ color: '#FEF3C7' }}>
{(performance.profit_factor || 0) >= 2.0 && '🔥 盈利能力出色每亏1元能赚' + (performance.profit_factor || 0).toFixed(1) + '元AI策略表现优异。'}
{(performance.profit_factor || 0) >= 1.5 && (performance.profit_factor || 0) < 2.0 && '✓ 策略稳定盈利,盈亏比健康,继续保持纪律性交易。'}
{(performance.profit_factor || 0) >= 1.0 && (performance.profit_factor || 0) < 1.5 && '⚠️ 策略略有盈利但需优化AI正在调整仓位和止损策略。'}
{(performance.profit_factor || 0) > 0 && (performance.profit_factor || 0) < 1.0 && '❌ 平均亏损大于盈利,需要调整策略或降低交易频率。'}
</div>
</div>
</div>
</div>
</div>
{/* 最佳/最差币种 - 独立行 */}
{(performance.best_symbol || performance.worst_symbol) && ( {(performance.best_symbol || performance.worst_symbol) && (
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{performance.best_symbol && ( {performance.best_symbol && (
<div className="rounded-xl p-5 backdrop-blur-sm" style={{ <div className="rounded-2xl p-6 backdrop-blur-sm" style={{
background: 'linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(14, 203, 129, 0.05) 100%)', background: 'linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(14, 203, 129, 0.05) 100%)',
border: '1px solid rgba(16, 185, 129, 0.3)', border: '1px solid rgba(16, 185, 129, 0.3)',
boxShadow: '0 4px 16px rgba(16, 185, 129, 0.1)' boxShadow: '0 4px 16px rgba(16, 185, 129, 0.1)'
}}> }}>
<div className="flex items-center gap-2 mb-3"> <div className="flex items-center gap-2 mb-3">
<span className="text-xl">🏆</span> <span className="text-2xl">🏆</span>
<span className="text-xs font-semibold" style={{ color: '#6EE7B7' }}>{t('bestPerformer', language)}</span> <span className="text-sm font-semibold" style={{ color: '#6EE7B7' }}>{t('bestPerformer', language)}</span>
</div> </div>
<div className="text-2xl font-bold mono mb-1" style={{ color: '#10B981' }}> <div className="text-3xl font-bold mono mb-1" style={{ color: '#10B981' }}>
{performance.best_symbol} {performance.best_symbol}
</div> </div>
{symbolStats[performance.best_symbol] && ( {symbolStats[performance.best_symbol] && (
<div className="text-sm font-semibold" style={{ color: '#6EE7B7' }}> <div className="text-lg font-semibold" style={{ color: '#6EE7B7' }}>
{symbolStats[performance.best_symbol].total_pn_l > 0 ? '+' : ''} {symbolStats[performance.best_symbol].total_pn_l > 0 ? '+' : ''}
{symbolStats[performance.best_symbol].total_pn_l.toFixed(2)}% {t('pnl', language)} {symbolStats[performance.best_symbol].total_pn_l.toFixed(2)}% {t('pnl', language)}
</div> </div>
@@ -314,20 +382,20 @@ export default function AILearning({ traderId }: AILearningProps) {
)} )}
{performance.worst_symbol && ( {performance.worst_symbol && (
<div className="rounded-xl p-5 backdrop-blur-sm" style={{ <div className="rounded-2xl p-6 backdrop-blur-sm" style={{
background: 'linear-gradient(135deg, rgba(248, 113, 113, 0.15) 0%, rgba(246, 70, 93, 0.05) 100%)', background: 'linear-gradient(135deg, rgba(248, 113, 113, 0.15) 0%, rgba(246, 70, 93, 0.05) 100%)',
border: '1px solid rgba(248, 113, 113, 0.3)', border: '1px solid rgba(248, 113, 113, 0.3)',
boxShadow: '0 4px 16px rgba(248, 113, 113, 0.1)' boxShadow: '0 4px 16px rgba(248, 113, 113, 0.1)'
}}> }}>
<div className="flex items-center gap-2 mb-3"> <div className="flex items-center gap-2 mb-3">
<span className="text-xl">📉</span> <span className="text-2xl">📉</span>
<span className="text-xs font-semibold" style={{ color: '#FCA5A5' }}>{t('worstPerformer', language)}</span> <span className="text-sm font-semibold" style={{ color: '#FCA5A5' }}>{t('worstPerformer', language)}</span>
</div> </div>
<div className="text-2xl font-bold mono mb-1" style={{ color: '#F87171' }}> <div className="text-3xl font-bold mono mb-1" style={{ color: '#F87171' }}>
{performance.worst_symbol} {performance.worst_symbol}
</div> </div>
{symbolStats[performance.worst_symbol] && ( {symbolStats[performance.worst_symbol] && (
<div className="text-sm font-semibold" style={{ color: '#FCA5A5' }}> <div className="text-lg font-semibold" style={{ color: '#FCA5A5' }}>
{symbolStats[performance.worst_symbol].total_pn_l > 0 ? '+' : ''} {symbolStats[performance.worst_symbol].total_pn_l > 0 ? '+' : ''}
{symbolStats[performance.worst_symbol].total_pn_l.toFixed(2)}% {t('pnl', language)} {symbolStats[performance.worst_symbol].total_pn_l.toFixed(2)}% {t('pnl', language)}
</div> </div>
@@ -337,22 +405,29 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
)} )}
{/* 币种表现统计 - 现代化表格 */} {/* 币种表现 & 历史成交 - 左右分屏 2列布局 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* 左侧:币种表现统计表格 */}
{symbolStatsList.length > 0 && ( {symbolStatsList.length > 0 && (
<div className="rounded-2xl overflow-hidden" style={{ <div className="rounded-2xl overflow-hidden" style={{
background: 'rgba(30, 35, 41, 0.4)', background: 'rgba(30, 35, 41, 0.4)',
border: '1px solid rgba(99, 102, 241, 0.2)', border: '1px solid rgba(99, 102, 241, 0.2)',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.2)' boxShadow: '0 4px 16px rgba(0, 0, 0, 0.2)',
maxHeight: 'calc(100vh - 200px)'
}}> }}>
<div className="p-5 border-b" style={{ borderColor: 'rgba(99, 102, 241, 0.2)', background: 'rgba(30, 35, 41, 0.6)' }}> <div className="p-5 border-b sticky top-0 z-10" style={{
<h3 className="font-bold flex items-center gap-2" style={{ color: '#E0E7FF' }}> borderColor: 'rgba(99, 102, 241, 0.2)',
{t('symbolPerformance', language)} background: 'rgba(30, 35, 41, 0.95)',
backdropFilter: 'blur(10px)'
}}>
<h3 className="font-bold flex items-center gap-2 text-lg" style={{ color: '#E0E7FF' }}>
📊 {t('symbolPerformance', language)}
</h3> </h3>
</div> </div>
<div className="overflow-x-auto"> <div className="overflow-y-auto" style={{ maxHeight: 'calc(100vh - 280px)' }}>
<table className="w-full"> <table className="w-full">
<thead> <thead className="sticky top-0 z-10">
<tr style={{ background: 'rgba(15, 23, 42, 0.6)' }}> <tr style={{ background: 'rgba(15, 23, 42, 0.95)', backdropFilter: 'blur(10px)' }}>
<th className="text-left px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Symbol</th> <th className="text-left px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Symbol</th>
<th className="text-right px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Trades</th> <th className="text-right px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Trades</th>
<th className="text-right px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Win Rate</th> <th className="text-right px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Win Rate</th>
@@ -394,25 +469,21 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
)} )}
</div> {/* 右侧:历史成交记录 */}
{/* 中间列结束 */} <div className="rounded-2xl overflow-hidden" style={{
{/* 右侧列:历史成交记录 (3列) */}
<div className="lg:col-span-3">
<div className="rounded-2xl overflow-hidden sticky top-24" style={{
background: 'rgba(30, 35, 41, 0.4)', background: 'rgba(30, 35, 41, 0.4)',
border: '1px solid rgba(240, 185, 11, 0.2)', border: '1px solid rgba(240, 185, 11, 0.2)',
maxHeight: 'calc(100vh - 200px)' maxHeight: 'calc(100vh - 200px)'
}}> }}>
{/* 标题 - 固定在顶部 */} <div className="p-5 border-b sticky top-0 z-10" style={{
<div className="p-4 border-b backdrop-blur-sm" style={{
background: 'rgba(240, 185, 11, 0.1)', background: 'rgba(240, 185, 11, 0.1)',
borderColor: 'rgba(240, 185, 11, 0.3)' borderColor: 'rgba(240, 185, 11, 0.3)',
backdropFilter: 'blur(10px)'
}}> }}>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-xl">📜</span> <span className="text-2xl">📜</span>
<div> <div>
<h3 className="font-bold text-sm" style={{ color: '#FCD34D' }}>{t('tradeHistory', language)}</h3> <h3 className="font-bold text-lg" style={{ color: '#FCD34D' }}>{t('tradeHistory', language)}</h3>
<p className="text-xs" style={{ color: '#94A3B8' }}> <p className="text-xs" style={{ color: '#94A3B8' }}>
{performance?.recent_trades && performance.recent_trades.length > 0 {performance?.recent_trades && performance.recent_trades.length > 0
? t('completedTrades', language, { count: performance.recent_trades.length }) ? t('completedTrades', language, { count: performance.recent_trades.length })
@@ -422,8 +493,7 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
</div> </div>
{/* 滚动内容区域 */} <div className="overflow-y-auto p-4 space-y-3" style={{ maxHeight: 'calc(100vh - 280px)' }}>
<div className="overflow-y-auto p-4 space-y-3" style={{ maxHeight: 'calc(100vh - 300px)' }}>
{performance?.recent_trades && performance.recent_trades.length > 0 ? ( {performance?.recent_trades && performance.recent_trades.length > 0 ? (
performance.recent_trades.map((trade: TradeOutcome, idx: number) => { performance.recent_trades.map((trade: TradeOutcome, idx: number) => {
const isProfitable = trade.pn_l >= 0; const isProfitable = trade.pn_l >= 0;
@@ -443,7 +513,6 @@ export default function AILearning({ traderId }: AILearningProps) {
? '0 4px 16px rgba(139, 92, 246, 0.2)' ? '0 4px 16px rgba(139, 92, 246, 0.2)'
: '0 2px 8px rgba(0, 0, 0, 0.1)' : '0 2px 8px rgba(0, 0, 0, 0.1)'
}}> }}>
{/* 头部:币种和方向 */}
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-base font-bold mono" style={{ color: '#E0E7FF' }}> <span className="text-base font-bold mono" style={{ color: '#E0E7FF' }}>
@@ -471,7 +540,6 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
</div> </div>
{/* 价格信息 */}
<div className="grid grid-cols-2 gap-2 mb-3 text-xs"> <div className="grid grid-cols-2 gap-2 mb-3 text-xs">
<div> <div>
<div style={{ color: '#94A3B8' }}>{t('entry', language)}</div> <div style={{ color: '#94A3B8' }}>{t('entry', language)}</div>
@@ -487,7 +555,6 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
</div> </div>
{/* 盈亏详情 */}
<div className="rounded-lg p-2 mb-2" style={{ <div className="rounded-lg p-2 mb-2" style={{
background: isProfitable ? 'rgba(16, 185, 129, 0.1)' : 'rgba(248, 113, 113, 0.1)' background: isProfitable ? 'rgba(16, 185, 129, 0.1)' : 'rgba(248, 113, 113, 0.1)'
}}> }}>
@@ -501,7 +568,6 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
</div> </div>
{/* 时间和持仓时长 */}
<div className="flex items-center justify-between text-xs" style={{ color: '#94A3B8' }}> <div className="flex items-center justify-between text-xs" style={{ color: '#94A3B8' }}>
<span> {formatDuration(trade.duration)}</span> <span> {formatDuration(trade.duration)}</span>
{trade.was_stop_loss && ( {trade.was_stop_loss && (
@@ -514,7 +580,6 @@ export default function AILearning({ traderId }: AILearningProps) {
)} )}
</div> </div>
{/* 交易时间 */}
<div className="text-xs mt-2 pt-2 border-t" style={{ <div className="text-xs mt-2 pt-2 border-t" style={{
color: '#64748B', color: '#64748B',
borderColor: 'rgba(71, 85, 105, 0.3)' borderColor: 'rgba(71, 85, 105, 0.3)'
@@ -538,10 +603,6 @@ export default function AILearning({ traderId }: AILearningProps) {
</div> </div>
</div> </div>
</div> </div>
{/* 右侧列结束 */}
</div>
{/* 3列布局结束 */}
{/* AI学习说明 - 现代化设计 */} {/* AI学习说明 - 现代化设计 */}
<div className="rounded-2xl p-6 backdrop-blur-sm" style={{ <div className="rounded-2xl p-6 backdrop-blur-sm" style={{
@@ -587,7 +648,6 @@ export default function AILearning({ traderId }: AILearningProps) {
function formatDuration(duration: string | undefined): string { function formatDuration(duration: string | undefined): string {
if (!duration) return '-'; if (!duration) return '-';
// duration格式: "1h23m45s" or "23m45.123s"
const match = duration.match(/(\d+h)?(\d+m)?(\d+\.?\d*s)?/); const match = duration.match(/(\d+h)?(\d+m)?(\d+\.?\d*s)?/);
if (!match) return duration; if (!match) return duration;
@@ -602,88 +662,3 @@ function formatDuration(duration: string | undefined): string {
return result || duration; return result || duration;
} }
// 从CoT trace中提取AI的历史表现分析和反思
function extractReflectionFromCoT(cotTrace: string | undefined): string | null {
if (!cotTrace) return null;
// 优先提取【历史经验反思】部分(新格式)
const reflectionMatch = cotTrace.match(/【历史经验反思】\s*([\s\S]*?)(?=【|$)/);
if (reflectionMatch) {
const reflection = reflectionMatch[1].trim();
if (reflection.length > 50) {
return `🎯 AI历史经验总结\n\n${reflection}`;
}
}
// 尝试提取"历史表现反馈"部分(兼容旧格式)
const performanceSectionMatch = cotTrace.match(/## 📊 历史表现反馈[\s\S]*?(?=##|$)/);
if (performanceSectionMatch) {
const performanceSection = performanceSectionMatch[0];
// 提取关键学习点
const lines: string[] = [];
// 提取总体统计
const statsMatch = performanceSection.match(/(\d+).*?([\d.]+)%.*?([\d.]+)/s);
if (statsMatch) {
const [, totalTrades, winRate, profitFactor] = statsMatch;
lines.push(`📈 历史表现回顾:`);
lines.push(` • 完成了 ${totalTrades} 笔交易,胜率 ${winRate}%`);
lines.push(` • 盈亏比 ${profitFactor}(平均盈利/平均亏损)`);
lines.push('');
}
// 提取最近交易
const recentTradesMatch = performanceSection.match(/最近5笔交易[\s\S]*?(?=##|表现最好|$)/);
if (recentTradesMatch) {
const tradesText = recentTradesMatch[0];
const tradeLines = tradesText.split('\n').filter(line => line.trim().startsWith('-'));
if (tradeLines.length > 0) {
lines.push(`🔍 最近交易分析:`);
tradeLines.slice(0, 3).forEach(line => {
lines.push(` ${line.trim()}`);
});
lines.push('');
}
}
// 提取最佳/最差币种
const bestWorstMatch = performanceSection.match(/([A-Z]+).*?\((.*?)\).*?([A-Z]+).*?\((.*?)\)/s);
if (bestWorstMatch) {
const [, bestSymbol, bestPnl, worstSymbol, worstPnl] = bestWorstMatch;
lines.push(`💡 币种表现洞察:`);
lines.push(` 🏆 ${bestSymbol} 表现最佳 ${bestPnl}`);
lines.push(` 💔 ${worstSymbol} 表现较差 ${worstPnl}`);
lines.push('');
}
// 尝试提取AI的分析或决策理由
const analysisMatch = cotTrace.match(/(?:分析|策略|决策)[:]([\s\S]*?)(?:\n\n|##|$)/);
if (analysisMatch) {
const analysis = analysisMatch[1].trim();
if (analysis.length > 20 && analysis.length < 500) {
lines.push(`🎯 AI策略调整`);
lines.push(` ${analysis.substring(0, 300)}${analysis.length > 300 ? '...' : ''}`);
}
}
if (lines.length > 0) {
return lines.join('\n');
}
}
// 如果没有找到历史表现反馈,尝试提取整体思路
const thinkingMatch = cotTrace.match(/(?:思考|分析|策略)[:]([\s\S]{50,500}?)(?:\n\n|##|决策|$)/);
if (thinkingMatch) {
return `🤔 AI思考过程\n\n${thinkingMatch[1].trim().substring(0, 400)}...`;
}
// 如果都没有返回CoT的前面部分
if (cotTrace.length > 100) {
return `💭 AI分析摘要\n\n${cotTrace.substring(0, 400).trim()}...`;
}
return null;
}