import useSWR from 'swr'; import { useLanguage } from '../contexts/LanguageContext'; import { t } from '../i18n/translations'; interface TradeOutcome { symbol: string; side: string; open_price: number; close_price: number; pn_l: number; pn_l_pct: number; duration: string; open_time: string; close_time: string; was_stop_loss: boolean; } interface SymbolPerformance { symbol: string; total_trades: number; winning_trades: number; losing_trades: number; win_rate: number; total_pn_l: number; avg_pn_l: number; } interface PerformanceAnalysis { total_trades: number; winning_trades: number; losing_trades: number; win_rate: number; avg_win: number; avg_loss: number; profit_factor: number; sharpe_ratio: number; // 夏普比率(风险调整后收益) recent_trades: TradeOutcome[]; symbol_stats: { [key: string]: SymbolPerformance }; best_symbol: string; worst_symbol: string; } interface AILearningProps { traderId: string; } interface DecisionRecord { timestamp: string; cycle_number: number; cot_trace: string; success: boolean; } const fetcher = (url: string) => fetch(url).then(res => res.json()); export default function AILearning({ traderId }: AILearningProps) { const { language } = useLanguage(); const { data: performance, error } = useSWR( `http://localhost:8080/api/performance?trader_id=${traderId}`, fetcher, { refreshInterval: 10000 } ); // 获取最新的决策记录,查看AI的思考过程 const { data: latestDecisions } = useSWR( `http://localhost:8080/api/decisions/latest?trader_id=${traderId}`, fetcher, { refreshInterval: 10000 } ); if (error) { return (
{t('loadingError', language)}
); } if (!performance) { return (
📊 {t('loading', language)}
); } if (!performance || performance.total_trades === 0) { return (
🧠

{t('aiLearning', language)}

{t('noCompleteData', language)}
); } // 安全地获取symbol_stats const symbolStats = performance.symbol_stats || {}; const symbolStatsList = Object.values(symbolStats).filter(stat => stat != null).sort( (a, b) => (b.total_pn_l || 0) - (a.total_pn_l || 0) ); // 提取AI的最新反思(从CoT trace中) const latestReflection = extractReflectionFromCoT(latestDecisions?.[0]?.cot_trace); return (
{/* 标题区 - 更简洁 */}
🧠

{t('aiLearning', language)}

{t('tradesAnalyzed', language, { count: performance.total_trades })}

{/* 主要内容:现代化网格布局 */}
{/* 左侧大区域:AI反思 + 核心指标 (5列) */}
{/* AI最新反思 - 渐变卡片 */} {latestReflection && latestDecisions && latestDecisions.length > 0 && (
{/* 背景装饰 */}
🎯

{t('latestReflection', language)}

{t('cycle', language)} #{latestDecisions[0].cycle_number} · {new Date(latestDecisions[0].timestamp).toLocaleTimeString()}

{latestReflection}
{latestDecisions[0].cot_trace && (
{t('fullCoT', language)}
{latestDecisions[0].cot_trace}
)}
)} {/* 核心指标网格 - 玻璃态设计 */}
{/* 总交易数 */}
{t('totalTrades', language)}
{performance.total_trades}
{/* 胜率 */}
= 50 ? 'rgba(14, 203, 129, 0.1)' : 'rgba(246, 70, 93, 0.1)', border: `1px solid ${(performance.win_rate || 0) >= 50 ? 'rgba(14, 203, 129, 0.3)' : 'rgba(246, 70, 93, 0.3)'}`, boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)' }}>
{t('winRate', language)}
= 50 ? '#10B981' : '#F87171' }}> {(performance.win_rate || 0).toFixed(1)}%
{performance.winning_trades || 0}W / {performance.losing_trades || 0}L
{/* 平均盈利 */}
{t('avgWin', language)}
+{(performance.avg_win || 0).toFixed(2)}%
{/* 平均亏损 */}
{t('avgLoss', language)}
{(performance.avg_loss || 0).toFixed(2)}%
{/* 夏普比率 - AI自我进化核心指标 */}
🧬
夏普比率
风险调整后收益 · AI自我进化指标
= 2 ? '#10B981' : (performance.sharpe_ratio || 0) >= 1 ? '#22D3EE' : (performance.sharpe_ratio || 0) >= 0 ? '#F0B90B' : '#F87171' }}> {performance.sharpe_ratio ? performance.sharpe_ratio.toFixed(2) : 'N/A'}
{performance.sharpe_ratio !== undefined && (
= 2 ? '#10B981' : (performance.sharpe_ratio || 0) >= 1 ? '#22D3EE' : (performance.sharpe_ratio || 0) >= 0 ? '#F0B90B' : '#F87171' }}> {performance.sharpe_ratio >= 2 ? '🟢 卓越表现' : performance.sharpe_ratio >= 1 ? '🟢 良好表现' : performance.sharpe_ratio >= 0 ? '🟡 波动较大' : '🔴 需要调整'}
)}
{performance.sharpe_ratio !== undefined && (
{performance.sharpe_ratio >= 2 && '✨ AI策略非常有效!风险调整后收益优异,可适度扩大仓位但保持纪律。'} {performance.sharpe_ratio >= 1 && performance.sharpe_ratio < 2 && '✅ 策略表现稳健,风险收益平衡良好,继续保持当前策略。'} {performance.sharpe_ratio >= 0 && performance.sharpe_ratio < 1 && '⚠️ 收益为正但波动较大,AI正在优化策略,降低风险。'} {performance.sharpe_ratio < 0 && '🚨 当前策略需要调整!AI已自动进入保守模式,减少仓位和交易频率。'}
)}
{/* 盈亏比 - 突出显示 */}
{t('profitFactor', language)}
{t('avgWinDivLoss', language)}
= 2.0 ? '#10B981' : (performance.profit_factor || 0) >= 1.5 ? '#F0B90B' : (performance.profit_factor || 0) >= 1.0 ? '#FB923C' : '#F87171' }}> {(performance.profit_factor || 0) > 0 ? (performance.profit_factor || 0).toFixed(2) : 'N/A'}
= 2.0 ? '#10B981' : (performance.profit_factor || 0) >= 1.5 ? '#F0B90B' : '#94A3B8' }}> {(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.0 && (performance.profit_factor || 0) < 1.5 && t('fair', language)} {(performance.profit_factor || 0) > 0 && (performance.profit_factor || 0) < 1.0 && t('poor', language)}
{/* 左侧结束 */} {/* 中间列:数据表格 (4列) */}
{/* 最佳/最差币种 */} {(performance.best_symbol || performance.worst_symbol) && (
{performance.best_symbol && (
🏆 {t('bestPerformer', language)}
{performance.best_symbol}
{symbolStats[performance.best_symbol] && (
{symbolStats[performance.best_symbol].total_pn_l > 0 ? '+' : ''} {symbolStats[performance.best_symbol].total_pn_l.toFixed(2)}% {t('pnl', language)}
)}
)} {performance.worst_symbol && (
📉 {t('worstPerformer', language)}
{performance.worst_symbol}
{symbolStats[performance.worst_symbol] && (
{symbolStats[performance.worst_symbol].total_pn_l > 0 ? '+' : ''} {symbolStats[performance.worst_symbol].total_pn_l.toFixed(2)}% {t('pnl', language)}
)}
)}
)} {/* 币种表现统计 - 现代化表格 */} {symbolStatsList.length > 0 && (

{t('symbolPerformance', language)}

{symbolStatsList.map((stat, idx) => ( 0 ? '1px solid rgba(99, 102, 241, 0.1)' : 'none' }}> ))}
Symbol Trades Win Rate Total P&L Avg P&L
{stat.symbol} {stat.total_trades} = 50 ? '#10B981' : '#F87171' }}> {(stat.win_rate || 0).toFixed(1)}% 0 ? '#10B981' : '#F87171' }}> {(stat.total_pn_l || 0) > 0 ? '+' : ''}{(stat.total_pn_l || 0).toFixed(2)}% 0 ? '#10B981' : '#F87171' }}> {(stat.avg_pn_l || 0) > 0 ? '+' : ''}{(stat.avg_pn_l || 0).toFixed(2)}%
)}
{/* 中间列结束 */} {/* 右侧列:历史成交记录 (3列) */}
{/* 标题 - 固定在顶部 */}
📜

{t('tradeHistory', language)}

{performance?.recent_trades && performance.recent_trades.length > 0 ? t('completedTrades', language, { count: performance.recent_trades.length }) : t('completedTradesWillAppear', language)}

{/* 滚动内容区域 */}
{performance?.recent_trades && performance.recent_trades.length > 0 ? ( performance.recent_trades.map((trade: TradeOutcome, idx: number) => { const isProfitable = trade.pn_l >= 0; const isRecent = idx === 0; return (
{/* 头部:币种和方向 */}
{trade.symbol} {trade.side.toUpperCase()} {isRecent && ( {t('latest', language)} )}
{isProfitable ? '+' : ''}{trade.pn_l_pct.toFixed(2)}%
{/* 价格信息 */}
{t('entry', language)}
{trade.open_price.toFixed(4)}
{t('exit', language)}
{trade.close_price.toFixed(4)}
{/* 盈亏详情 */}
P&L {isProfitable ? '+' : ''}{trade.pn_l.toFixed(2)} USDT
{/* 时间和持仓时长 */}
⏱️ {formatDuration(trade.duration)} {trade.was_stop_loss && ( {t('stopLoss', language)} )}
{/* 交易时间 */}
{new Date(trade.close_time).toLocaleString('en-US', { month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' })}
); }) ) : (
📜
{t('noCompletedTrades', language)}
)}
{/* 右侧列结束 */}
{/* 3列布局结束 */} {/* AI学习说明 - 现代化设计 */}
💡

{t('howAILearns', language)}

{t('aiLearningPoint1', language)}
{t('aiLearningPoint2', language)}
{t('aiLearningPoint3', language)}
{t('aiLearningPoint4', language)}
); } // 格式化持仓时长 function formatDuration(duration: string | undefined): string { if (!duration) return '-'; // duration格式: "1h23m45s" or "23m45.123s" const match = duration.match(/(\d+h)?(\d+m)?(\d+\.?\d*s)?/); if (!match) return duration; const hours = match[1] || ''; const minutes = match[2] || ''; const seconds = match[3] || ''; let result = ''; if (hours) result += hours.replace('h', '小时'); if (minutes) result += minutes.replace('m', '分'); if (!hours && seconds) result += seconds.replace(/(\d+)\.?\d*s/, '$1秒'); 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; }