feat(ui): update frontend components and agent prompts
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled

- Update analyst, portfolio_manager, risk_manager prompts
- Enhance analysis tools functionality
- Improve UI components (AgentCard, AgentFeed, RoomView, etc.)
- Update constants configuration

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-03-13 04:31:06 +08:00
parent 679431b303
commit 7fbfc96795
14 changed files with 273 additions and 224 deletions

View File

@@ -1,13 +1,13 @@
# Analyst Personas Configuration
# 分析师角色配置
fundamentals_analyst:
name: "Fundamental Analyst"
name: "基本面分析师"
focus:
- "Company financial health and profitability"
- "Business model sustainability and competitive advantages"
- "Management quality and corporate governance"
- "Industry position and market share"
- "Long-term investment value assessment"
- "公司财务健康状况和盈利能力"
- "商业模式可持续性和竞争优势"
- "管理层质量和公司治理"
- "行业地位和市场份额"
- "长期投资价值评估"
tools:
- "analyze_profitability"
- "analyze_growth"
@@ -15,30 +15,30 @@ fundamentals_analyst:
- "analyze_valuation_ratios"
- "analyze_efficiency_ratios"
description: |
As a fundamental analyst, you focus on:
- Company financial health and profitability
- Business model sustainability and competitive advantages
- Management quality and corporate governance
- Industry position and market share
- Long-term investment value assessment
You tend to select tools that provide deep insights into company intrinsic value, preferring fundamental and valuation tools.
作为基本面分析师,你专注于:
- 公司财务健康状况和盈利能力
- 商业模式可持续性和竞争优势
- 管理层质量和公司治理
- 行业地位和市场份额
- 长期投资价值评估
你倾向于选择能够深入了解公司内在价值的工具,更偏好基本面和估值类工具。
technical_analyst:
name: "Technical Analyst"
name: "技术分析师"
focus:
- "Price trends and chart patterns"
- "Technical indicators and trading signals"
- "Market sentiment and capital flows"
- "Support/resistance levels and key price points"
- "Short to medium-term trading opportunities"
- "价格趋势和图表形态"
- "技术指标和交易信号"
- "市场情绪和资金流向"
- "支撑/阻力位和关键价格点"
- "中短期交易机会"
description: |
As a technical analyst, you focus on:
- Price trends and chart patterns
- Technical indicators and trading signals
- Market sentiment and capital flows
- Support/resistance levels and key price points
- Short to medium-term trading opportunities
You tend to select tools that capture price dynamics and market trends, preferring technical analysis tools.
作为技术分析师,你专注于:
- 价格趋势和图表形态
- 技术指标和交易信号
- 市场情绪和资金流向
- 支撑/阻力位和关键价格点
- 中短期交易机会
你倾向于选择能够捕捉价格动态和市场趋势的工具,更偏好技术分析类工具。
tools:
- "analyze_trend_following"
- "analyze_momentum"
@@ -46,41 +46,41 @@ technical_analyst:
- "analyze_volatility"
sentiment_analyst:
name: "Sentiment Analyst"
name: "情绪分析师"
focus:
- "Market participant sentiment changes"
- "News opinion and media influence"
- "Insider trading behavior"
- "Investor panic and greed emotions"
- "Market expectations and psychological factors"
- "市场参与者情绪变化"
- "新闻舆情和媒体影响"
- "内部人交易行为"
- "投资者恐慌和贪婪情绪"
- "市场预期和心理因素"
description: |
As a sentiment analyst, you focus on:
- Market participant sentiment changes
- News opinion and media influence
- Insider trading behavior
- Investor panic and greed emotions
- Market expectations and psychological factors
You tend to select tools that reflect market sentiment and investor behavior, preferring sentiment and behavioral tools.
作为情绪分析师,你专注于:
- 市场参与者情绪变化
- 新闻舆情和媒体影响
- 内部人交易行为
- 投资者恐慌和贪婪情绪
- 市场预期和心理因素
你倾向于选择能够反映市场情绪和投资者行为的工具,更偏好情绪和行为类工具。
tools:
- "analyze_news_sentiment"
- "analyze_insider_trading"
valuation_analyst:
name: "Valuation Analyst"
name: "估值分析师"
focus:
- "Company intrinsic value calculation"
- "Comparison of different valuation methods"
- "Valuation model assumptions and sensitivity"
- "Relative and absolute valuation"
- "Investment margin of safety assessment"
- "公司内在价值计算"
- "不同估值方法的比较"
- "估值模型假设和敏感性分析"
- "相对估值和绝对估值"
- "投资安全边际评估"
description: |
As a valuation analyst, you focus on:
- Company intrinsic value calculation
- Comparison of different valuation methods
- Valuation model assumptions and sensitivity
- Relative and absolute valuation
- Investment margin of safety assessment
You tend to select tools that accurately calculate company value, preferring valuation models and fundamental tools.
作为估值分析师,你专注于:
- 公司内在价值计算
- 不同估值方法的比较
- 估值模型假设和敏感性分析
- 相对估值和绝对估值
- 投资安全边际评估
你倾向于选择能够准确计算公司价值的工具,更偏好估值模型和基本面工具。
tools:
- "dcf_valuation_analysis"
- "owner_earnings_valuation_analysis"
@@ -88,21 +88,21 @@ valuation_analyst:
- "residual_income_valuation_analysis"
comprehensive_analyst:
name: "Comprehensive Analyst"
name: "综合分析师"
focus:
- "Integrate multiple analytical perspectives"
- "Balance short-term and long-term factors"
- "Comprehensively consider fundamentals, technicals, and sentiment"
- "Provide comprehensive investment advice"
- "Adapt to different market environments"
- "整合多种分析视角"
- "平衡短期和长期因素"
- "综合考虑基本面、技术面和情绪面"
- "提供全面的投资建议"
- "适应不同市场环境"
description: |
As a comprehensive analyst, you need to:
- Integrate multiple analytical perspectives
- Balance short-term and long-term factors
- Consider combined impact of fundamentals, technicals, and sentiment
- Provide comprehensive investment advice
- Adapt to different market environments
You will flexibly select various tools based on specific situations, pursuing comprehensiveness and accuracy in analysis.
作为综合分析师,你需要:
- 整合多种分析视角
- 平衡短期和长期因素
- 综合考虑基本面、技术面和情绪面的影响
- 提供全面的投资建议
- 适应不同市场环境
你会根据具体情况灵活选择各类工具,追求分析的全面性和准确性。
tools:
- "analyze_profitability"
- "analyze_growth"

View File

@@ -1,23 +1,23 @@
You are a professional {{ analyst_type }}.
你是一位专业的{{ analyst_type }}
Your Focus:
你的关注重点:
{{ focus }}
Your Role:
你的角色:
{{ description }}
Note:
- Construct and continuously refine your "Investment Philosophy." Your analyses should not be isolated events but rather manifestations of your overarching worldview and core investment beliefs. After each analysis, you must reflect:
- How did this case/data validate or challenge your existing conviction?
- What key principle regarding markets, human psychology, valuation, or risk management did you learn from this mistake (or success)?
- Deepen your "Investment Logic." Ensure every investment recommendation you make is supported by a clear, traceable, and repeatable logic. Your analysis steps should resemble a rigorous proof, covering:
- Core Driver Identification: What are the genuine variables that influence value?
- Risk Boundary Setting: Under what specific scenarios would your recommendation fail?
- Contrarian Testing: What is the prevailing market consensus, and where is your view differentiated?
Maintain Humility and Openness. A core trait of an Investment Master is continuous learning and adaptation. In every analysis, you must actively seek out evidence and arguments that contradict your own view and integrate them into your final assessment.
- You have access to analysis tools. Use them to gather relevant data and make informed recommendations.
注意:
- 构建并持续完善你的"投资哲学"。你的分析不应是孤立的事件,而应该是你整体投资世界观和核心信念的体现。每次分析后,你必须反思:
- 这个案例/数据如何验证或挑战了你现有的信念?
- 你从这次错误(或成功)中学到了关于市场、人性、估值或风险管理的什么关键原则?
- 深化你的"投资逻辑"。确保每一项投资建议都有清晰、可追溯、可重复的逻辑支撑。你的分析步骤应该像严谨的证明一样,涵盖:
- 核心驱动因素识别:真正影响价值的变量是什么?
- 风险边界设定:在什么具体情况下你的建议会失效?
- 逆向测试:市场主流共识是什么,你的观点有何不同?
保持谦逊和开放。投资大师的核心特质是持续学习和适应。在每次分析中,你必须积极寻找与自己观点相悖的证据和论据,并将其纳入最终评估。
- 你可以使用分析工具。用它们来收集相关数据并做出明智的建议。
Output Guidelines:
- Return clear investment signals: bullish, bearish, or neutral
- Include confidence level (0-100)
- Provide reasoning for your analysis (Present your conclusion first if you are sure to share your final analysis. )
输出指南:
- 给出明确的投资信号:看涨、看跌或中性
- 包含置信度(0-100
- 为你的分析提供理由(如果你确定要分享最终分析,请先给出结论)

View File

@@ -1,31 +1,31 @@
You are a Portfolio Manager responsible for making investment decisions.
你是一位负责做出投资决策的投资组合经理。
Your Core Responsibilities:
1. Analyze input from analysts and risk managers
2. Make investment decisions based on signals and market context
3. Record your decisions using the available tool
你的核心职责:
1. 分析分析师和风险管理经理的输入
2. 基于信号和市场情境做出投资决策
3. 使用可用工具记录你的决策
Decision Framework:
- Review analysis to understand market views
- Consider risk warnings before making decisions
- Evaluate current portfolio positions and cash
- Make decisions that align with the portfolio's investment objectives
决策框架:
- 审阅分析以了解市场观点
- 在做决策前考虑风险警告
- 评估当前投资组合持仓和现金
- 做出与投资组合投资目标一致的决策
Decision Types:
- "long": Bullish - recommend buying shares
- "short": Bearish - recommend selling shares or shorting
- "hold": Neutral - maintain current positions
决策类型:
- "long":看涨 - 建议买入股票
- "short":看跌 - 建议卖出股票或做空
- "hold":中性 - 维持当前持仓
Budget Awareness:
- Consider available cash when deciding quantities
- Do not recommend buying more than cash allows
- Consider margin requirements for short positions
预算意识:
- 在决定数量时考虑可用现金
- 不要建议买入超过现金允许的数量
- 考虑做空头寸的保证金要求
Output:
Use the `make_decision` tool to record your decision for each ticker.
After recording all decisions, provide a summary of your investment rationale.
输出:
使用 `make_decision` 工具记录你对每个股票代码的决策。
记录所有决策后,提供你的投资逻辑总结。
Important:
- Base decisions on the analyst signals and risk assessments provided
- Be conservative with position sizes relative to portfolio value
- Always provide reasoning for your decisions
重要:
- 基于提供的分析师信号和风险评估做出决策
- 相对于投资组合价值保持保守的仓位规模
- 始终为你的决策提供理由

View File

@@ -1,21 +1,18 @@
You are a professional Risk Manager responsible for monitoring portfolio risk and providing risk warnings.
Your Core Responsibilities:
1. Monitor portfolio exposure and concentration risk
2. Evaluate position sizes relative to volatility
3. Assess margin usage and leverage levels
4. Identify potential risk factors and provide warnings
5. Suggest position limits based on market conditions
Your Decision Process:
3. Generate actionable risk warnings and position limit recommendations
4. Provide clear reasoning for your risk assessments
Output Guidelines:
- Be concise but thorough in risk assessments
- Prioritize warnings by severity
- Provide specific, actionable recommendations
- Include quantitative metrics when available
你是一位专业的风险管理经理,负责监控投资组合风险并提供风险警告。
你的核心职责:
1. 监控投资组合敞口和集中度风险
2. 评估仓位规模相对于波动性
3. 评估保证金使用和杠杆水平
4. 识别潜在风险因素并提供警告
5. 基于市场条件建议仓位限制
你的决策流程:
3. 生成可操作的风险警告和仓位限制建议
4. 为你的风险评估提供清晰的理由
输出指南:
- 风险评估要简洁但全面
- 按严重程度优先排序警告
- 提供具体、可操作的建议
- 尽可能包含量化指标

View File

@@ -7,12 +7,13 @@ Returns human-readable text format for easy LLM consumption.
"""
# flake8: noqa: E501
# pylint: disable=C0301,W0613
import json
import logging
import traceback
from datetime import datetime, timedelta
from functools import wraps
from statistics import median
from typing import List, Optional
from typing import List, Optional, Union
import numpy as np
import pandas as pd
@@ -37,6 +38,39 @@ def _to_text_response(text: str) -> ToolResponse:
return ToolResponse(content=[TextBlock(type="text", text=text)])
def _parse_tickers(tickers: Union[str, List[str], None]) -> List[str]:
"""
Parse tickers parameter which may be a JSON string or a list.
LLM sometimes passes tickers as a JSON string like '["AAPL", "MSFT"]'
instead of an actual list. This function handles both cases.
Args:
tickers: List of stock tickers as a list or JSON string.
Returns:
List of stock tickers.
"""
if tickers is None:
return []
if isinstance(tickers, str):
try:
parsed = json.loads(tickers)
if isinstance(parsed, list):
return parsed
# If it's a single string, wrap in list
return [parsed]
except json.JSONDecodeError:
# If not valid JSON, treat as comma-separated string
return [t.strip() for t in tickers.split(",") if t.strip()]
if isinstance(tickers, list):
return tickers
return []
def _safe_float(value, default=0.0) -> float:
"""Safely convert to float."""
try:
@@ -100,6 +134,7 @@ def analyze_efficiency_ratios(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Efficiency Ratios Analysis ({current_date}) ===\n"]
for ticker in tickers:
@@ -141,6 +176,7 @@ def analyze_profitability(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Profitability Analysis ({current_date}) ===\n"]
for ticker in tickers:
@@ -182,6 +218,7 @@ def analyze_growth(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Growth Analysis ({current_date}) ===\n"]
for ticker in tickers:
@@ -222,6 +259,7 @@ def analyze_financial_health(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Financial Health Analysis ({current_date}) ===\n"]
for ticker in tickers:
@@ -265,6 +303,7 @@ def analyze_valuation_ratios(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Valuation Ratios Analysis ({current_date}) ===\n"]
for ticker in tickers:
@@ -364,6 +403,7 @@ def analyze_trend_following(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Trend Following Analysis ({current_date}) ===\n"]
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
@@ -459,6 +499,7 @@ def analyze_mean_reversion(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Mean Reversion Analysis ({current_date}) ===\n"]
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
@@ -545,6 +586,7 @@ def analyze_momentum(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Momentum Analysis ({current_date}) ===\n"]
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
@@ -640,6 +682,7 @@ def analyze_volatility(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Volatility Analysis ({current_date}) ===\n"]
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
@@ -718,6 +761,7 @@ def analyze_insider_trading(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Insider Trading Analysis ({current_date}) ===\n"]
for ticker in tickers:
@@ -786,6 +830,7 @@ def analyze_news_sentiment(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== News Analysis ({current_date}) ===\n"]
for ticker in tickers:
@@ -835,6 +880,7 @@ def dcf_valuation_analysis(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== DCF Valuation Analysis ({current_date}) ===\n"]
for ticker in tickers:
@@ -931,6 +977,7 @@ def owner_earnings_valuation_analysis(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Owner Earnings Valuation ({current_date}) ===\n"]
for ticker in tickers:
@@ -1049,6 +1096,7 @@ def ev_ebitda_valuation_analysis(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== EV/EBITDA Valuation ({current_date}) ===\n"]
for ticker in tickers:
@@ -1144,6 +1192,7 @@ def residual_income_valuation_analysis(
"""
current_date = _resolved_date(current_date)
tickers = _parse_tickers(tickers)
lines = [f"=== Residual Income Valuation ({current_date}) ===\n"]
for ticker in tickers:

View File

@@ -755,7 +755,7 @@ export default function LiveTradingApp() {
fontFamily: '"Courier New", monospace',
letterSpacing: '0.5px'
}}>
LIVE MOCK MODE
模拟模式
</span>
</div>
)}
@@ -785,7 +785,7 @@ export default function LiveTradingApp() {
textTransform: 'uppercase',
letterSpacing: '0.5px'
}}>
VIRTUAL TIME
虚拟时间
</span>
<span style={{
fontSize: '14px',
@@ -854,13 +854,13 @@ export default function LiveTradingApp() {
<div className="header-status-inline">
<span className={`status-dot ${isConnected ? (isUpdating ? 'updating' : 'live') : 'offline'}`} />
<span className={`status-text ${isConnected ? 'live' : 'offline'}`}>
{isConnected ? (isUpdating ? 'SYNCING' : 'LIVE') : 'OFFLINE'}
{isConnected ? (isUpdating ? '同步中' : '在线') : '离线'}
</span>
{marketStatus && (
<>
<span className="status-sep">·</span>
<span className={`market-text ${serverMode === 'backtest' ? 'backtest' : (marketStatus.status === 'open' ? 'open' : 'closed')}`}>
{marketStatus.status_text || (marketStatus.status === 'open' ? 'OPEN' : 'CLOSED')}
{marketStatus.status_text || (marketStatus.status === 'open' ? '开盘' : '收盘')}
</span>
</>
)}
@@ -903,7 +903,7 @@ export default function LiveTradingApp() {
))}
</div>
<div className="portfolio-value">
<span className="portfolio-label">PORTFOLIO</span>
<span className="portfolio-label">投资组合</span>
<span className="portfolio-amount">${formatNumber(portfolioData.netValue)}</span>
</div>
</div>
@@ -918,28 +918,28 @@ export default function LiveTradingApp() {
className={`view-nav-btn ${currentView === 'rules' ? 'active' : ''}`}
onClick={() => setCurrentView('rules')}
>
Rules
规则
</button>
<button
className={`view-nav-btn ${currentView === 'room' ? 'active' : ''}`}
onClick={() => setCurrentView('room')}
>
Trading Room
交易室
</button>
<button
className={`view-nav-btn ${currentView === 'chart' ? 'active' : ''}`}
onClick={() => setCurrentView('chart')}
>
Performance Chart
业绩图表
</button>
<button
className={`view-nav-btn ${currentView === 'statistics' ? 'active' : ''}`}
onClick={() => setCurrentView('statistics')}
>
Statistics
统计
</button>
</div>
@@ -970,7 +970,7 @@ export default function LiveTradingApp() {
className={`chart-tab ${chartTab === 'all' ? 'active' : ''}`}
onClick={() => setChartTab('all')}
>
Daily
日线
</button>
{/* <button
className={`chart-tab ${chartTab === 'live' ? 'active' : ''} ${!isLiveEnabled ? 'disabled' : ''}`}

View File

@@ -110,12 +110,15 @@ export default function AboutModal({ onClose }) {
zh: {
intro: "如果不是让模型彼此竞争,而是像一支高效协作的团队一样进行实时交易,会发生什么?",
question: "这里不是竞技场而是团队。我们希望Agents不再单打独斗而是「组团」进入实时金融市场——这一十分困难且充满噪声的环境。",
trying: "我们正在探索多智能体协作在实时金融交易中的可能性。",
title1: "✦ 多智能体的技能互补",
point1: "不同模型、不同角色的智能体像真实的金融团队一样协作,各自承担数据分析、策略生成、风险控制等职责。",
point1Sub: "通过通知和会议机制进行信息交换,实现高效协作。",
title2: "✦ 能够持续进化的智能体系统",
point2: "依托「记忆」模块每个智能体都能跨回合保留经验不断学习、反思与调整。我们希望能看到在长期实时交易中Agent形成自己的独特方法论而不是一次性偶然的推理。",
point2Sub: "ReMe 记忆框架帮助 Agents 持续改进。",
title3: "✦ 实时参与市场的 AI Agents",
point3: "Agents从实时行情中学习并给予即时决策不是纸上谈兵而是面对市场的真实波动。"

View File

@@ -6,9 +6,9 @@ import { getModelIcon, getShortModelName } from '../utils/modelIcons';
* Get rank medal/trophy
*/
function getRankMedal(rank) {
if (rank === 1) return { emoji: '🏆', color: '#FFD700', label: 'Gold' };
if (rank === 2) return { emoji: '🥈', color: '#C0C0C0', label: 'Silver' };
if (rank === 3) return { emoji: '🥉', color: '#CD7F32', label: 'Bronze' };
if (rank === 1) return { emoji: '🏆', color: '#FFD700', label: '金牌' };
if (rank === 2) return { emoji: '🥈', color: '#C0C0C0', label: '银牌' };
if (rank === 3) return { emoji: '🥉', color: '#CD7F32', label: '铜牌' };
return { emoji: `#${rank}`, color: '#333333', label: `#${rank}` };
}
@@ -41,7 +41,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
const rankMedal = agent.rank ? getRankMedal(agent.rank) : null;
const isPortfolioManager = agent.id === 'portfolio_manager';
const isRiskManager = agent.id === 'risk_manager';
const displayName = isPortfolioManager ? 'Team' : agent.name;
const displayName = isPortfolioManager ? '团队' : agent.name;
// Get model icon configuration
const modelInfo = getModelIcon(agent.modelName, agent.modelProvider);
@@ -149,7 +149,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
whiteSpace: 'normal',
wordWrap: 'break-word'
}}>
Risk Manager focuses on risk management and does not participate in prediction accuracy ranking.
风控经理专注于风险管理不参与预测准确率排名
</div>
</div>
)}
@@ -173,7 +173,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
whiteSpace: 'normal',
wordWrap: 'break-word'
}}>
Portfolio Manager provides the team's final signal(position), synthesizing all analyst recommendations, and does not participate in ranking.
投资经理综合所有分析师建议提供团队最终交易信号不参与排名
</div>
</div>
)}
@@ -197,7 +197,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
marginBottom: 4,
textTransform: 'uppercase'
}}>
Model
模型
</div>
<div style={{
height: 40,
@@ -262,7 +262,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
marginBottom: 4,
textTransform: 'uppercase'
}}>
Win Rate
胜率
</div>
<div style={{
fontSize: 36,
@@ -278,7 +278,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
fontSize: 9,
color: '#555555'
}}>
{bullWins + bearWins}Win / {evaluatedTotal}Eval
{bullWins + bearWins} / {evaluatedTotal}
</div>
<div style={{
fontSize: 8,
@@ -288,7 +288,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
lineHeight: 1.2,
whiteSpace: 'pre-line'
}}>
Eval: total evaluated bull & bear signals.{'\n'}Win Rate = correct signals / total evaluated signals
评估: 总评估多空信号数{'\n'}胜率 = 正确信号 / 总评估信号
</div>
</div>
)}
@@ -309,7 +309,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
marginBottom: 4,
textTransform: 'uppercase'
}}>
Bull Win Rate
牛市胜率
</div>
<div style={{
fontSize: 28,
@@ -324,7 +324,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
fontSize: 9,
color: '#333333'
}}>
{bullWins}Win / {evaluatedBull}Eval
{bullWins} / {evaluatedBull}
{bullUnknown > 0 && ` / ${bullUnknown}P`}
</div>
</div>
@@ -346,7 +346,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
marginBottom: 4,
textTransform: 'uppercase'
}}>
Bear Win Rate
熊市胜率
</div>
<div style={{
fontSize: 28,
@@ -361,7 +361,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
fontSize: 9,
color: '#333333'
}}>
{bearWins}Win / {evaluatedBear}Eval
{bearWins} / {evaluatedBear}
{bearUnknown > 0 && ` / ${bearUnknown}P`}
</div>
</div>
@@ -433,7 +433,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
fontSize: 16,
color: isBull ? '#00C853' : isBear ? '#FF1744' : '#555555'
}}>
{isBull ? 'bull' : isBear ? 'bear' : 'neutral'}
{isBull ? '看涨' : isBear ? '看跌' : '中性'}
</div>
<div style={{
fontSize: 8,
@@ -471,14 +471,14 @@ export default function AgentCard({ agent, onClose, isClosing }) {
fontWeight: 700,
color: '#1976D2'
}}>
ⓘ Info
说明
</div>
<div style={{
fontSize: 8,
color: '#1976D2',
lineHeight: 1.2
}}>
Showing recent 5 trading days (1 week) signals only
仅显示最近5个交易日(1)的信号
</div>
</div>
</div>

View File

@@ -161,9 +161,9 @@ const AgentFeed = forwardRef(({ feed, leaderboard }, ref) => {
return (
<div className="agent-feed">
<div className="agent-feed-header">
<h3 className="agent-feed-title">ACTIVITY FEED</h3>
<h3 className="agent-feed-title">活动 feed</h3>
<div className="agent-filter-wrapper">
<label className="agent-filter-label">Filter:</label>
<label className="agent-filter-label">筛选:</label>
<div className="custom-select-wrapper">
<button
className="custom-select-trigger"
@@ -191,7 +191,7 @@ const AgentFeed = forwardRef(({ feed, leaderboard }, ref) => {
setDropdownOpen(false);
}}
>
<span>All Agents</span>
<span>全部 Agents</span>
</div>
{uniqueAgents.map(agent => {
const agentInfo = getAgentInfoByName(agent);
@@ -225,8 +225,8 @@ const AgentFeed = forwardRef(({ feed, leaderboard }, ref) => {
{filteredFeed.length === 0 && (
<div className="empty-state">
{selectedAgent === 'all'
? 'Waiting for system updates...'
: `No messages from ${selectedAgent}`}
? '等待系统更新...'
: `${selectedAgent} 没有消息`}
</div>
)}
@@ -294,9 +294,9 @@ function ConferenceItem({ conference, itemId, isHighlighted, getAgentModelInfo }
>
<div className="feed-item-header">
<span className="feed-item-title" style={{ color: colors.text }}>
CONFERENCE
会议
</span>
{conference.isLive && <span className="feed-live-badge"> LIVE</span>}
{conference.isLive && <span className="feed-live-badge"> 实时</span>}
<span className="feed-item-time">{formatTime(conference.startTime)}</span>
</div>
@@ -366,7 +366,7 @@ function ConferenceMessage({ message, getAgentModelInfo }) {
className="conf-expand-btn"
onClick={() => setExpanded(!expanded)}
>
{expanded ? Less' : 'More »'}
{expanded ? 收起' : '更多 »'}
</button>
)}
</div>
@@ -397,8 +397,8 @@ function MemoryItem({ memory, itemId, isHighlighted }) {
}
const agentLabel = memory.agent && memory.agent !== 'Memory'
? `MEMORY · ${memory.agent}`
: 'MEMORY';
? `记忆 · ${memory.agent}`
: '记忆';
return (
<div
@@ -511,7 +511,7 @@ function MemoryItem({ memory, itemId, isHighlighted }) {
className="feed-expand-btn"
onClick={() => setExpanded(!expanded)}
>
{expanded ? Less' : 'More »'}
{expanded ? 收起' : '更多 »'}
</button>
)}
</div>
@@ -525,7 +525,7 @@ function MessageItem({ message, itemId, isHighlighted, getAgentModelInfo }) {
const colors = message.agent === 'Memory' ? MESSAGE_COLORS.memory :
getAgentColors(message.agentId, message.agent);
const title = message.agent === 'Memory' ? 'MEMORY' : message.agent || 'AGENT';
const title = message.agent === 'Memory' ? '记忆' : message.agent || 'AGENT';
const agentModelData = message.agentId && getAgentModelInfo ?
getAgentModelInfo(message.agentId) :
@@ -614,7 +614,7 @@ function MessageItem({ message, itemId, isHighlighted, getAgentModelInfo }) {
outline: 'none'
}}
>
📄 {isManagerAgent ? 'View decision log »' : 'View full report »'}
📄 {isManagerAgent ? '查看决策日志 »' : '查看完整报告 »'}
</button>
)}
@@ -623,7 +623,7 @@ function MessageItem({ message, itemId, isHighlighted, getAgentModelInfo }) {
className="feed-expand-btn"
onClick={() => setExpanded(!expanded)}
>
{expanded ? Less' : 'More »'}
{expanded ? 收起' : '更多 »'}
</button>
)}
</div>

View File

@@ -59,7 +59,7 @@ export default function Header({
letterSpacing: '0.5px',
marginRight: '0px'
}}>
OPEN SOURCE
开源
</span>
<a
@@ -131,7 +131,7 @@ export default function Header({
}}
>
<span className="header-link">
Contact Us
联系我们
</span>
{/* Two contact buttons */}

View File

@@ -551,7 +551,7 @@ export default function RoomView({ bubbles, bubbleFor, leaderboard, feed, onJump
{/* Hint Text */}
<div className="agent-hint-text">
Click avatar to view details
点击头像查看详情
</div>
</div>
@@ -620,14 +620,14 @@ export default function RoomView({ bubbles, bubbleFor, leaderboard, feed, onJump
<button
className="bubble-jump-btn"
onClick={handleJumpToFeed}
title="Jump to message in feed"
title="跳转到消息"
>
</button>
<button
className="bubble-close-btn"
onClick={(e) => handleCloseBubble(agent.id, bubbleKey, e)}
title="Close bubble"
title="关闭"
>
×
</button>
@@ -728,7 +728,7 @@ export default function RoomView({ bubbles, bubbleFor, leaderboard, feed, onJump
title="Replay feed history"
>
<span className="replay-icon">&#9654;&#9654;</span>
<span>REPLAY</span>
<span>回放</span>
</button>
</div>
)}
@@ -749,7 +749,7 @@ export default function RoomView({ bubbles, bubbleFor, leaderboard, feed, onJump
}}
/>
<div className="replay-indicator">
<span className="replay-status">{isPaused ? 'PAUSED' : 'REPLAY MODE'}</span>
<span className="replay-status">{isPaused ? '已暂停' : '回放模式'}</span>
<button
className="replay-button"
onClick={isPaused ? resumeReplay : pauseReplay}

View File

@@ -181,17 +181,17 @@ export default function RulesView() {
},
zh: {
section1Title: "Agent 设定",
pmRole: "Portfolio Manager",
pmRole: "投资经理",
pmDesc: "负责最终交易决策和团队协作",
rmRole: "Risk Manager",
rmRole: "风控经理",
rmDesc: "监控组合风险并执行风险限制",
analystsRole: "Analysts",
analystsRole: "分析师",
analystsDesc: "使用不同工具和 AI 模型进行专业研究:",
analysts: [
{ name: "Valuation Analyst", model: "Moonshot", modelKey: "Moonshot" },
{ name: "Sentiment Analyst", model: "Qwen", modelKey: "Alibaba" },
{ name: "Fundamentals Analyst", model: "DeepSeek", modelKey: "DeepSeek" },
{ name: "Technical Analyst", model: "Zhipu AI", modelKey: "Zhipu AI" }
{ name: "估值分析师", model: "Moonshot", modelKey: "Moonshot" },
{ name: "情绪分析师", model: "Qwen", modelKey: "Alibaba" },
{ name: "基本面分析师", model: "DeepSeek", modelKey: "DeepSeek" },
{ name: "技术分析师", model: "Zhipu AI", modelKey: "Zhipu AI" }
],
section2Title: "Agent 决策机制",

View File

@@ -151,7 +151,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
color: '#000000',
textTransform: 'uppercase'
}}>
Performance
业绩表现
</h2>
</div>
@@ -170,7 +170,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
marginBottom: 12,
textTransform: 'uppercase'
}}>
Total Asset Value
总资产价值
</div>
<div style={{
fontSize: 36,
@@ -202,7 +202,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
marginBottom: 8,
textTransform: 'uppercase'
}}>
Excess Return
超额收益
</div>
<div style={{
fontSize: 28,
@@ -218,7 +218,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
marginTop: 4,
fontFamily: '"Courier New", monospace'
}}>
vs. VW: {excessReturnData.benchmarkReturn >= 0 ? '+' : ''}{excessReturnData.benchmarkReturn.toFixed(2)}%
vs 市值加权: {excessReturnData.benchmarkReturn >= 0 ? '+' : ''}{excessReturnData.benchmarkReturn.toFixed(2)}%
</div>
</div>
) : null}
@@ -233,7 +233,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
marginBottom: 8,
textTransform: 'uppercase'
}}>
Win Rate
胜率
</div>
<div style={{
fontSize: 28,
@@ -252,7 +252,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
marginTop: 4,
fontFamily: '"Courier New", monospace'
}}>
{pmWinRateData.totalWins}Win / {pmWinRateData.evaluatedTotal}Eval
{pmWinRateData.totalWins} / {pmWinRateData.evaluatedTotal}
</div>
)}
</div>
@@ -267,7 +267,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
marginBottom: 8,
textTransform: 'uppercase'
}}>
Absolute Return
绝对收益
</div>
<div style={{
fontSize: 28,
@@ -296,7 +296,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
letterSpacing: 0.5,
textTransform: 'uppercase'
}}>
Cash Position
现金头寸
</div>
<div style={{
fontSize: 16,
@@ -322,7 +322,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
letterSpacing: 0.5,
textTransform: 'uppercase'
}}>
Total Trades
总交易数
</div>
<div style={{
fontSize: 16,
@@ -350,7 +350,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
textTransform: 'uppercase',
color: '#666666'
}}>
Portfolio Weights
组合权重
</div>
<div className="statistics-table-container" style={{
display: 'grid',
@@ -397,7 +397,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
fontSize: 12,
letterSpacing: 0.5
}}>
No statistics available
暂无统计数据
</div>
)}
</div>
@@ -433,7 +433,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
color: '#000000',
textTransform: 'uppercase'
}}>
Portfolio Holdings
持仓明细
</h2>
</div>
@@ -448,7 +448,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
fontSize: 11,
letterSpacing: 0.5
}}>
No positions currently held
当前无持仓
</div>
) : (
<>
@@ -456,11 +456,11 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
<table className="data-table">
<thead>
<tr>
<th>Ticker</th>
<th>Quantity</th>
<th>Price</th>
<th>Value</th>
<th>Weight</th>
<th>代码</th>
<th>数量</th>
<th>价格</th>
<th>市值</th>
<th>权重</th>
</tr>
</thead>
<tbody>
@@ -505,7 +505,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
onClick={() => setHoldingsPage(p => Math.max(1, p - 1))}
disabled={holdingsPage === 1}
>
Prev
上一页
</button>
<div className="pagination-info">
@@ -517,7 +517,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
onClick={() => setHoldingsPage(p => Math.min(totalHoldingsPages, p + 1))}
disabled={holdingsPage === totalHoldingsPages}
>
Next
下一页
</button>
</div>
)}
@@ -552,7 +552,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
color: '#000000',
textTransform: 'uppercase'
}}>
Transaction History
交易历史
</h2>
{trades.length > 0 && (
<div style={{
@@ -560,7 +560,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
color: '#666666',
fontFamily: '"Courier New", monospace'
}}>
{trades.length} total
{trades.length}
</div>
)}
</div>
@@ -576,7 +576,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
fontSize: 11,
letterSpacing: 0.5
}}>
No trades recorded
暂无交易记录
</div>
) : (
<>
@@ -584,11 +584,11 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
<table className="data-table">
<thead>
<tr>
<th>Time</th>
<th>Stock</th>
<th>Side</th>
<th>Qty</th>
<th>Price</th>
<th>时间</th>
<th>股票</th>
<th>方向</th>
<th>数量</th>
<th>价格</th>
</tr>
</thead>
<tbody>
@@ -638,7 +638,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
onClick={() => setTradesPage(p => Math.max(1, p - 1))}
disabled={tradesPage === 1}
>
Prev
上一页
</button>
<div className="pagination-info">
@@ -650,7 +650,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
onClick={() => setTradesPage(p => Math.min(totalTradesPages, p + 1))}
disabled={tradesPage === totalTradesPages}
>
Next
下一页
</button>
</div>
)}

View File

@@ -66,43 +66,43 @@ export const AGENT_SEATS = [
export const AGENTS = [
{
id: "portfolio_manager",
name: "Portfolio Manager",
role: "Portfolio Manager",
name: "投资经理",
role: "投资经理",
avatar: CDN_ASSETS.companyRoom.agent_1,
colors: { bg: "#F9FDFF", text: "#1565C0", accent: "#1565C0" }
},
{
id: "risk_manager",
name: "Risk Manager",
role: "Risk Manager",
name: "风控经理",
role: "风控经理",
avatar: CDN_ASSETS.companyRoom.agent_2,
colors: { bg: "#FFF8F8", text: "#C62828", accent: "#C62828" }
},
{
id: "valuation_analyst",
name: "Valuation Analyst",
role: "Valuation Analyst",
name: "估值分析师",
role: "估值分析师",
avatar: CDN_ASSETS.companyRoom.agent_3,
colors: { bg: "#FAFFFA", text: "#2E7D32", accent: "#2E7D32" }
},
{
id: "sentiment_analyst",
name: "Sentiment Analyst",
role: "Sentiment Analyst",
name: "情绪分析师",
role: "情绪分析师",
avatar: CDN_ASSETS.companyRoom.agent_4,
colors: { bg: "#FCFAFF", text: "#6A1B9A", accent: "#6A1B9A" }
},
{
id: "fundamentals_analyst",
name: "Fundamentals Analyst",
role: "Fundamentals Analyst",
name: "基本面分析师",
role: "基本面分析师",
avatar: CDN_ASSETS.companyRoom.agent_5,
colors: { bg: "#FFFCF7", text: "#E65100", accent: "#E65100" }
},
{
id: "technical_analyst",
name: "Technical Analyst",
role: "Technical Analyst",
name: "技术分析师",
role: "技术分析师",
avatar: CDN_ASSETS.companyRoom.agent_6,
colors: { bg: "#F9FEFF", text: "#00838F", accent: "#00838F" }
},