feat(ui): update frontend components and agent prompts
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
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:
@@ -1,13 +1,13 @@
|
|||||||
# Analyst Personas Configuration
|
# 分析师角色配置
|
||||||
|
|
||||||
fundamentals_analyst:
|
fundamentals_analyst:
|
||||||
name: "Fundamental Analyst"
|
name: "基本面分析师"
|
||||||
focus:
|
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:
|
tools:
|
||||||
- "analyze_profitability"
|
- "analyze_profitability"
|
||||||
- "analyze_growth"
|
- "analyze_growth"
|
||||||
@@ -15,30 +15,30 @@ fundamentals_analyst:
|
|||||||
- "analyze_valuation_ratios"
|
- "analyze_valuation_ratios"
|
||||||
- "analyze_efficiency_ratios"
|
- "analyze_efficiency_ratios"
|
||||||
description: |
|
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:
|
technical_analyst:
|
||||||
name: "Technical Analyst"
|
name: "技术分析师"
|
||||||
focus:
|
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: |
|
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:
|
tools:
|
||||||
- "analyze_trend_following"
|
- "analyze_trend_following"
|
||||||
- "analyze_momentum"
|
- "analyze_momentum"
|
||||||
@@ -46,41 +46,41 @@ technical_analyst:
|
|||||||
- "analyze_volatility"
|
- "analyze_volatility"
|
||||||
|
|
||||||
sentiment_analyst:
|
sentiment_analyst:
|
||||||
name: "Sentiment Analyst"
|
name: "情绪分析师"
|
||||||
focus:
|
focus:
|
||||||
- "Market participant sentiment changes"
|
- "市场参与者情绪变化"
|
||||||
- "News opinion and media influence"
|
- "新闻舆情和媒体影响"
|
||||||
- "Insider trading behavior"
|
- "内部人交易行为"
|
||||||
- "Investor panic and greed emotions"
|
- "投资者恐慌和贪婪情绪"
|
||||||
- "Market expectations and psychological factors"
|
- "市场预期和心理因素"
|
||||||
description: |
|
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:
|
tools:
|
||||||
- "analyze_news_sentiment"
|
- "analyze_news_sentiment"
|
||||||
- "analyze_insider_trading"
|
- "analyze_insider_trading"
|
||||||
|
|
||||||
valuation_analyst:
|
valuation_analyst:
|
||||||
name: "Valuation Analyst"
|
name: "估值分析师"
|
||||||
focus:
|
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: |
|
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:
|
tools:
|
||||||
- "dcf_valuation_analysis"
|
- "dcf_valuation_analysis"
|
||||||
- "owner_earnings_valuation_analysis"
|
- "owner_earnings_valuation_analysis"
|
||||||
@@ -88,21 +88,21 @@ valuation_analyst:
|
|||||||
- "residual_income_valuation_analysis"
|
- "residual_income_valuation_analysis"
|
||||||
|
|
||||||
comprehensive_analyst:
|
comprehensive_analyst:
|
||||||
name: "Comprehensive Analyst"
|
name: "综合分析师"
|
||||||
focus:
|
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: |
|
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:
|
tools:
|
||||||
- "analyze_profitability"
|
- "analyze_profitability"
|
||||||
- "analyze_growth"
|
- "analyze_growth"
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
You are a professional {{ analyst_type }}.
|
你是一位专业的{{ analyst_type }}。
|
||||||
|
|
||||||
Your Focus:
|
你的关注重点:
|
||||||
{{ focus }}
|
{{ focus }}
|
||||||
|
|
||||||
Your Role:
|
你的角色:
|
||||||
{{ description }}
|
{{ 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)
|
- 包含置信度(0-100)
|
||||||
- Provide reasoning for your analysis (Present your conclusion first if you are sure to share your final analysis. )
|
- 为你的分析提供理由(如果你确定要分享最终分析,请先给出结论)
|
||||||
|
|||||||
@@ -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
|
1. 分析分析师和风险管理经理的输入
|
||||||
2. Make investment decisions based on signals and market context
|
2. 基于信号和市场情境做出投资决策
|
||||||
3. Record your decisions using the available tool
|
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
|
- "long":看涨 - 建议买入股票
|
||||||
- "short": Bearish - recommend selling shares or shorting
|
- "short":看跌 - 建议卖出股票或做空
|
||||||
- "hold": Neutral - maintain current positions
|
- "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.
|
使用 `make_decision` 工具记录你对每个股票代码的决策。
|
||||||
After recording all decisions, provide a summary of your investment rationale.
|
记录所有决策后,提供你的投资逻辑总结。
|
||||||
|
|
||||||
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
|
- 始终为你的决策提供理由
|
||||||
|
|||||||
@@ -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. 为你的风险评估提供清晰的理由
|
||||||
|
|
||||||
|
输出指南:
|
||||||
|
- 风险评估要简洁但全面
|
||||||
|
- 按严重程度优先排序警告
|
||||||
|
- 提供具体、可操作的建议
|
||||||
|
- 尽可能包含量化指标
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ Returns human-readable text format for easy LLM consumption.
|
|||||||
"""
|
"""
|
||||||
# flake8: noqa: E501
|
# flake8: noqa: E501
|
||||||
# pylint: disable=C0301,W0613
|
# pylint: disable=C0301,W0613
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from statistics import median
|
from statistics import median
|
||||||
from typing import List, Optional
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
@@ -37,6 +38,39 @@ def _to_text_response(text: str) -> ToolResponse:
|
|||||||
return ToolResponse(content=[TextBlock(type="text", text=text)])
|
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:
|
def _safe_float(value, default=0.0) -> float:
|
||||||
"""Safely convert to float."""
|
"""Safely convert to float."""
|
||||||
try:
|
try:
|
||||||
@@ -100,6 +134,7 @@ def analyze_efficiency_ratios(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Efficiency Ratios Analysis ({current_date}) ===\n"]
|
lines = [f"=== Efficiency Ratios Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -141,6 +176,7 @@ def analyze_profitability(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Profitability Analysis ({current_date}) ===\n"]
|
lines = [f"=== Profitability Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -182,6 +218,7 @@ def analyze_growth(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Growth Analysis ({current_date}) ===\n"]
|
lines = [f"=== Growth Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -222,6 +259,7 @@ def analyze_financial_health(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Financial Health Analysis ({current_date}) ===\n"]
|
lines = [f"=== Financial Health Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -265,6 +303,7 @@ def analyze_valuation_ratios(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Valuation Ratios Analysis ({current_date}) ===\n"]
|
lines = [f"=== Valuation Ratios Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -364,6 +403,7 @@ def analyze_trend_following(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Trend Following Analysis ({current_date}) ===\n"]
|
lines = [f"=== Trend Following Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
|
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
|
||||||
@@ -459,6 +499,7 @@ def analyze_mean_reversion(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Mean Reversion Analysis ({current_date}) ===\n"]
|
lines = [f"=== Mean Reversion Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
|
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
|
||||||
@@ -545,6 +586,7 @@ def analyze_momentum(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Momentum Analysis ({current_date}) ===\n"]
|
lines = [f"=== Momentum Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
|
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
|
||||||
@@ -640,6 +682,7 @@ def analyze_volatility(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Volatility Analysis ({current_date}) ===\n"]
|
lines = [f"=== Volatility Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
|
end_dt = datetime.strptime(current_date, "%Y-%m-%d")
|
||||||
@@ -718,6 +761,7 @@ def analyze_insider_trading(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Insider Trading Analysis ({current_date}) ===\n"]
|
lines = [f"=== Insider Trading Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -786,6 +830,7 @@ def analyze_news_sentiment(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== News Analysis ({current_date}) ===\n"]
|
lines = [f"=== News Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -835,6 +880,7 @@ def dcf_valuation_analysis(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== DCF Valuation Analysis ({current_date}) ===\n"]
|
lines = [f"=== DCF Valuation Analysis ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -931,6 +977,7 @@ def owner_earnings_valuation_analysis(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Owner Earnings Valuation ({current_date}) ===\n"]
|
lines = [f"=== Owner Earnings Valuation ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -1049,6 +1096,7 @@ def ev_ebitda_valuation_analysis(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== EV/EBITDA Valuation ({current_date}) ===\n"]
|
lines = [f"=== EV/EBITDA Valuation ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
@@ -1144,6 +1192,7 @@ def residual_income_valuation_analysis(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
current_date = _resolved_date(current_date)
|
current_date = _resolved_date(current_date)
|
||||||
|
tickers = _parse_tickers(tickers)
|
||||||
lines = [f"=== Residual Income Valuation ({current_date}) ===\n"]
|
lines = [f"=== Residual Income Valuation ({current_date}) ===\n"]
|
||||||
|
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
|
|||||||
@@ -755,7 +755,7 @@ export default function LiveTradingApp() {
|
|||||||
fontFamily: '"Courier New", monospace',
|
fontFamily: '"Courier New", monospace',
|
||||||
letterSpacing: '0.5px'
|
letterSpacing: '0.5px'
|
||||||
}}>
|
}}>
|
||||||
LIVE MOCK MODE
|
模拟模式
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -785,7 +785,7 @@ export default function LiveTradingApp() {
|
|||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: '0.5px'
|
letterSpacing: '0.5px'
|
||||||
}}>
|
}}>
|
||||||
VIRTUAL TIME
|
虚拟时间
|
||||||
</span>
|
</span>
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
@@ -854,13 +854,13 @@ export default function LiveTradingApp() {
|
|||||||
<div className="header-status-inline">
|
<div className="header-status-inline">
|
||||||
<span className={`status-dot ${isConnected ? (isUpdating ? 'updating' : 'live') : 'offline'}`} />
|
<span className={`status-dot ${isConnected ? (isUpdating ? 'updating' : 'live') : 'offline'}`} />
|
||||||
<span className={`status-text ${isConnected ? 'live' : 'offline'}`}>
|
<span className={`status-text ${isConnected ? 'live' : 'offline'}`}>
|
||||||
{isConnected ? (isUpdating ? 'SYNCING' : 'LIVE') : 'OFFLINE'}
|
{isConnected ? (isUpdating ? '同步中' : '在线') : '离线'}
|
||||||
</span>
|
</span>
|
||||||
{marketStatus && (
|
{marketStatus && (
|
||||||
<>
|
<>
|
||||||
<span className="status-sep">·</span>
|
<span className="status-sep">·</span>
|
||||||
<span className={`market-text ${serverMode === 'backtest' ? 'backtest' : (marketStatus.status === 'open' ? 'open' : 'closed')}`}>
|
<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>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -903,7 +903,7 @@ export default function LiveTradingApp() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="portfolio-value">
|
<div className="portfolio-value">
|
||||||
<span className="portfolio-label">PORTFOLIO</span>
|
<span className="portfolio-label">投资组合</span>
|
||||||
<span className="portfolio-amount">${formatNumber(portfolioData.netValue)}</span>
|
<span className="portfolio-amount">${formatNumber(portfolioData.netValue)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -918,28 +918,28 @@ export default function LiveTradingApp() {
|
|||||||
className={`view-nav-btn ${currentView === 'rules' ? 'active' : ''}`}
|
className={`view-nav-btn ${currentView === 'rules' ? 'active' : ''}`}
|
||||||
onClick={() => setCurrentView('rules')}
|
onClick={() => setCurrentView('rules')}
|
||||||
>
|
>
|
||||||
Rules
|
规则
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={`view-nav-btn ${currentView === 'room' ? 'active' : ''}`}
|
className={`view-nav-btn ${currentView === 'room' ? 'active' : ''}`}
|
||||||
onClick={() => setCurrentView('room')}
|
onClick={() => setCurrentView('room')}
|
||||||
>
|
>
|
||||||
Trading Room
|
交易室
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={`view-nav-btn ${currentView === 'chart' ? 'active' : ''}`}
|
className={`view-nav-btn ${currentView === 'chart' ? 'active' : ''}`}
|
||||||
onClick={() => setCurrentView('chart')}
|
onClick={() => setCurrentView('chart')}
|
||||||
>
|
>
|
||||||
Performance Chart
|
业绩图表
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={`view-nav-btn ${currentView === 'statistics' ? 'active' : ''}`}
|
className={`view-nav-btn ${currentView === 'statistics' ? 'active' : ''}`}
|
||||||
onClick={() => setCurrentView('statistics')}
|
onClick={() => setCurrentView('statistics')}
|
||||||
>
|
>
|
||||||
Statistics
|
统计
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -970,7 +970,7 @@ export default function LiveTradingApp() {
|
|||||||
className={`chart-tab ${chartTab === 'all' ? 'active' : ''}`}
|
className={`chart-tab ${chartTab === 'all' ? 'active' : ''}`}
|
||||||
onClick={() => setChartTab('all')}
|
onClick={() => setChartTab('all')}
|
||||||
>
|
>
|
||||||
Daily
|
日线
|
||||||
</button>
|
</button>
|
||||||
{/* <button
|
{/* <button
|
||||||
className={`chart-tab ${chartTab === 'live' ? 'active' : ''} ${!isLiveEnabled ? 'disabled' : ''}`}
|
className={`chart-tab ${chartTab === 'live' ? 'active' : ''} ${!isLiveEnabled ? 'disabled' : ''}`}
|
||||||
|
|||||||
@@ -110,12 +110,15 @@ export default function AboutModal({ onClose }) {
|
|||||||
zh: {
|
zh: {
|
||||||
intro: "如果不是让模型彼此竞争,而是像一支高效协作的团队一样进行实时交易,会发生什么?",
|
intro: "如果不是让模型彼此竞争,而是像一支高效协作的团队一样进行实时交易,会发生什么?",
|
||||||
question: "这里不是竞技场,而是团队。我们希望Agents不再单打独斗,而是「组团」进入实时金融市场——这一十分困难且充满噪声的环境。",
|
question: "这里不是竞技场,而是团队。我们希望Agents不再单打独斗,而是「组团」进入实时金融市场——这一十分困难且充满噪声的环境。",
|
||||||
|
trying: "我们正在探索多智能体协作在实时金融交易中的可能性。",
|
||||||
|
|
||||||
title1: "✦ 多智能体的技能互补",
|
title1: "✦ 多智能体的技能互补",
|
||||||
point1: "不同模型、不同角色的智能体像真实的金融团队一样协作,各自承担数据分析、策略生成、风险控制等职责。",
|
point1: "不同模型、不同角色的智能体像真实的金融团队一样协作,各自承担数据分析、策略生成、风险控制等职责。",
|
||||||
|
point1Sub: "通过通知和会议机制进行信息交换,实现高效协作。",
|
||||||
|
|
||||||
title2: "✦ 能够持续进化的智能体系统",
|
title2: "✦ 能够持续进化的智能体系统",
|
||||||
point2: "依托「记忆」模块,每个智能体都能跨回合保留经验,不断学习、反思与调整。我们希望能看到在长期实时交易中,Agent形成自己的独特方法论,而不是一次性偶然的推理。",
|
point2: "依托「记忆」模块,每个智能体都能跨回合保留经验,不断学习、反思与调整。我们希望能看到在长期实时交易中,Agent形成自己的独特方法论,而不是一次性偶然的推理。",
|
||||||
|
point2Sub: "ReMe 记忆框架帮助 Agents 持续改进。",
|
||||||
|
|
||||||
title3: "✦ 实时参与市场的 AI Agents",
|
title3: "✦ 实时参与市场的 AI Agents",
|
||||||
point3: "Agents从实时行情中学习,并给予即时决策;不是纸上谈兵,而是面对市场的真实波动。"
|
point3: "Agents从实时行情中学习,并给予即时决策;不是纸上谈兵,而是面对市场的真实波动。"
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { getModelIcon, getShortModelName } from '../utils/modelIcons';
|
|||||||
* Get rank medal/trophy
|
* Get rank medal/trophy
|
||||||
*/
|
*/
|
||||||
function getRankMedal(rank) {
|
function getRankMedal(rank) {
|
||||||
if (rank === 1) return { emoji: '🏆', color: '#FFD700', label: 'Gold' };
|
if (rank === 1) return { emoji: '🏆', color: '#FFD700', label: '金牌' };
|
||||||
if (rank === 2) return { emoji: '🥈', color: '#C0C0C0', label: 'Silver' };
|
if (rank === 2) return { emoji: '🥈', color: '#C0C0C0', label: '银牌' };
|
||||||
if (rank === 3) return { emoji: '🥉', color: '#CD7F32', label: 'Bronze' };
|
if (rank === 3) return { emoji: '🥉', color: '#CD7F32', label: '铜牌' };
|
||||||
return { emoji: `#${rank}`, color: '#333333', label: `#${rank}` };
|
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 rankMedal = agent.rank ? getRankMedal(agent.rank) : null;
|
||||||
const isPortfolioManager = agent.id === 'portfolio_manager';
|
const isPortfolioManager = agent.id === 'portfolio_manager';
|
||||||
const isRiskManager = agent.id === 'risk_manager';
|
const isRiskManager = agent.id === 'risk_manager';
|
||||||
const displayName = isPortfolioManager ? 'Team' : agent.name;
|
const displayName = isPortfolioManager ? '团队' : agent.name;
|
||||||
|
|
||||||
// Get model icon configuration
|
// Get model icon configuration
|
||||||
const modelInfo = getModelIcon(agent.modelName, agent.modelProvider);
|
const modelInfo = getModelIcon(agent.modelName, agent.modelProvider);
|
||||||
@@ -149,7 +149,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
whiteSpace: 'normal',
|
whiteSpace: 'normal',
|
||||||
wordWrap: 'break-word'
|
wordWrap: 'break-word'
|
||||||
}}>
|
}}>
|
||||||
ⓘ Risk Manager focuses on risk management and does not participate in prediction accuracy ranking.
|
ⓘ 风控经理专注于风险管理,不参与预测准确率排名。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -173,7 +173,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
whiteSpace: 'normal',
|
whiteSpace: 'normal',
|
||||||
wordWrap: 'break-word'
|
wordWrap: 'break-word'
|
||||||
}}>
|
}}>
|
||||||
ⓘ Portfolio Manager provides the team's final signal(position), synthesizing all analyst recommendations, and does not participate in ranking.
|
ⓘ 投资经理综合所有分析师建议,提供团队最终交易信号,不参与排名。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -197,7 +197,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Model
|
模型
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
height: 40,
|
height: 40,
|
||||||
@@ -262,7 +262,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Win Rate
|
胜率
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 36,
|
fontSize: 36,
|
||||||
@@ -278,7 +278,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
color: '#555555'
|
color: '#555555'
|
||||||
}}>
|
}}>
|
||||||
{bullWins + bearWins}Win / {evaluatedTotal}Eval
|
{bullWins + bearWins}胜 / {evaluatedTotal}评
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
@@ -288,7 +288,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
lineHeight: 1.2,
|
lineHeight: 1.2,
|
||||||
whiteSpace: 'pre-line'
|
whiteSpace: 'pre-line'
|
||||||
}}>
|
}}>
|
||||||
Eval: total evaluated bull & bear signals.{'\n'}Win Rate = correct signals / total evaluated signals
|
评估: 总评估多空信号数。{'\n'}胜率 = 正确信号 / 总评估信号
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -309,7 +309,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Bull Win Rate
|
牛市胜率
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
@@ -324,7 +324,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
color: '#333333'
|
color: '#333333'
|
||||||
}}>
|
}}>
|
||||||
{bullWins}Win / {evaluatedBull}Eval
|
{bullWins}胜 / {evaluatedBull}评
|
||||||
{bullUnknown > 0 && ` / ${bullUnknown}P`}
|
{bullUnknown > 0 && ` / ${bullUnknown}P`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -346,7 +346,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Bear Win Rate
|
熊市胜率
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
@@ -361,7 +361,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
color: '#333333'
|
color: '#333333'
|
||||||
}}>
|
}}>
|
||||||
{bearWins}Win / {evaluatedBear}Eval
|
{bearWins}胜 / {evaluatedBear}评
|
||||||
{bearUnknown > 0 && ` / ${bearUnknown}P`}
|
{bearUnknown > 0 && ` / ${bearUnknown}P`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -433,7 +433,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: isBull ? '#00C853' : isBear ? '#FF1744' : '#555555'
|
color: isBull ? '#00C853' : isBear ? '#FF1744' : '#555555'
|
||||||
}}>
|
}}>
|
||||||
{isBull ? 'bull' : isBear ? 'bear' : 'neutral'}
|
{isBull ? '看涨' : isBear ? '看跌' : '中性'}
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
@@ -471,14 +471,14 @@ export default function AgentCard({ agent, onClose, isClosing }) {
|
|||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
color: '#1976D2'
|
color: '#1976D2'
|
||||||
}}>
|
}}>
|
||||||
ⓘ Info
|
ⓘ 说明
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
color: '#1976D2',
|
color: '#1976D2',
|
||||||
lineHeight: 1.2
|
lineHeight: 1.2
|
||||||
}}>
|
}}>
|
||||||
Showing recent 5 trading days (1 week) signals only
|
仅显示最近5个交易日(1周)的信号
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -161,9 +161,9 @@ const AgentFeed = forwardRef(({ feed, leaderboard }, ref) => {
|
|||||||
return (
|
return (
|
||||||
<div className="agent-feed">
|
<div className="agent-feed">
|
||||||
<div className="agent-feed-header">
|
<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">
|
<div className="agent-filter-wrapper">
|
||||||
<label className="agent-filter-label">Filter:</label>
|
<label className="agent-filter-label">筛选:</label>
|
||||||
<div className="custom-select-wrapper">
|
<div className="custom-select-wrapper">
|
||||||
<button
|
<button
|
||||||
className="custom-select-trigger"
|
className="custom-select-trigger"
|
||||||
@@ -191,7 +191,7 @@ const AgentFeed = forwardRef(({ feed, leaderboard }, ref) => {
|
|||||||
setDropdownOpen(false);
|
setDropdownOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span>All Agents</span>
|
<span>全部 Agents</span>
|
||||||
</div>
|
</div>
|
||||||
{uniqueAgents.map(agent => {
|
{uniqueAgents.map(agent => {
|
||||||
const agentInfo = getAgentInfoByName(agent);
|
const agentInfo = getAgentInfoByName(agent);
|
||||||
@@ -225,8 +225,8 @@ const AgentFeed = forwardRef(({ feed, leaderboard }, ref) => {
|
|||||||
{filteredFeed.length === 0 && (
|
{filteredFeed.length === 0 && (
|
||||||
<div className="empty-state">
|
<div className="empty-state">
|
||||||
{selectedAgent === 'all'
|
{selectedAgent === 'all'
|
||||||
? 'Waiting for system updates...'
|
? '等待系统更新...'
|
||||||
: `No messages from ${selectedAgent}`}
|
: `${selectedAgent} 没有消息`}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -294,9 +294,9 @@ function ConferenceItem({ conference, itemId, isHighlighted, getAgentModelInfo }
|
|||||||
>
|
>
|
||||||
<div className="feed-item-header">
|
<div className="feed-item-header">
|
||||||
<span className="feed-item-title" style={{ color: colors.text }}>
|
<span className="feed-item-title" style={{ color: colors.text }}>
|
||||||
CONFERENCE
|
会议
|
||||||
</span>
|
</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>
|
<span className="feed-item-time">{formatTime(conference.startTime)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -366,7 +366,7 @@ function ConferenceMessage({ message, getAgentModelInfo }) {
|
|||||||
className="conf-expand-btn"
|
className="conf-expand-btn"
|
||||||
onClick={() => setExpanded(!expanded)}
|
onClick={() => setExpanded(!expanded)}
|
||||||
>
|
>
|
||||||
{expanded ? '« Less' : 'More »'}
|
{expanded ? '« 收起' : '更多 »'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -397,8 +397,8 @@ function MemoryItem({ memory, itemId, isHighlighted }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const agentLabel = memory.agent && memory.agent !== 'Memory'
|
const agentLabel = memory.agent && memory.agent !== 'Memory'
|
||||||
? `MEMORY · ${memory.agent}`
|
? `记忆 · ${memory.agent}`
|
||||||
: 'MEMORY';
|
: '记忆';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -511,7 +511,7 @@ function MemoryItem({ memory, itemId, isHighlighted }) {
|
|||||||
className="feed-expand-btn"
|
className="feed-expand-btn"
|
||||||
onClick={() => setExpanded(!expanded)}
|
onClick={() => setExpanded(!expanded)}
|
||||||
>
|
>
|
||||||
{expanded ? '« Less' : 'More »'}
|
{expanded ? '« 收起' : '更多 »'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -525,7 +525,7 @@ function MessageItem({ message, itemId, isHighlighted, getAgentModelInfo }) {
|
|||||||
|
|
||||||
const colors = message.agent === 'Memory' ? MESSAGE_COLORS.memory :
|
const colors = message.agent === 'Memory' ? MESSAGE_COLORS.memory :
|
||||||
getAgentColors(message.agentId, message.agent);
|
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 ?
|
const agentModelData = message.agentId && getAgentModelInfo ?
|
||||||
getAgentModelInfo(message.agentId) :
|
getAgentModelInfo(message.agentId) :
|
||||||
@@ -614,7 +614,7 @@ function MessageItem({ message, itemId, isHighlighted, getAgentModelInfo }) {
|
|||||||
outline: 'none'
|
outline: 'none'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
📄 {isManagerAgent ? 'View decision log »' : 'View full report »'}
|
📄 {isManagerAgent ? '查看决策日志 »' : '查看完整报告 »'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -623,7 +623,7 @@ function MessageItem({ message, itemId, isHighlighted, getAgentModelInfo }) {
|
|||||||
className="feed-expand-btn"
|
className="feed-expand-btn"
|
||||||
onClick={() => setExpanded(!expanded)}
|
onClick={() => setExpanded(!expanded)}
|
||||||
>
|
>
|
||||||
{expanded ? '« Less' : 'More »'}
|
{expanded ? '« 收起' : '更多 »'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export default function Header({
|
|||||||
letterSpacing: '0.5px',
|
letterSpacing: '0.5px',
|
||||||
marginRight: '0px'
|
marginRight: '0px'
|
||||||
}}>
|
}}>
|
||||||
OPEN SOURCE
|
开源
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
@@ -131,7 +131,7 @@ export default function Header({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="header-link">
|
<span className="header-link">
|
||||||
Contact Us
|
联系我们
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Two contact buttons */}
|
{/* Two contact buttons */}
|
||||||
|
|||||||
@@ -551,7 +551,7 @@ export default function RoomView({ bubbles, bubbleFor, leaderboard, feed, onJump
|
|||||||
|
|
||||||
{/* Hint Text */}
|
{/* Hint Text */}
|
||||||
<div className="agent-hint-text">
|
<div className="agent-hint-text">
|
||||||
Click avatar to view details
|
点击头像查看详情
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -620,14 +620,14 @@ export default function RoomView({ bubbles, bubbleFor, leaderboard, feed, onJump
|
|||||||
<button
|
<button
|
||||||
className="bubble-jump-btn"
|
className="bubble-jump-btn"
|
||||||
onClick={handleJumpToFeed}
|
onClick={handleJumpToFeed}
|
||||||
title="Jump to message in feed"
|
title="跳转到消息"
|
||||||
>
|
>
|
||||||
↗
|
↗
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="bubble-close-btn"
|
className="bubble-close-btn"
|
||||||
onClick={(e) => handleCloseBubble(agent.id, bubbleKey, e)}
|
onClick={(e) => handleCloseBubble(agent.id, bubbleKey, e)}
|
||||||
title="Close bubble"
|
title="关闭"
|
||||||
>
|
>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
@@ -728,7 +728,7 @@ export default function RoomView({ bubbles, bubbleFor, leaderboard, feed, onJump
|
|||||||
title="Replay feed history"
|
title="Replay feed history"
|
||||||
>
|
>
|
||||||
<span className="replay-icon">▶▶</span>
|
<span className="replay-icon">▶▶</span>
|
||||||
<span>REPLAY</span>
|
<span>回放</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -749,7 +749,7 @@ export default function RoomView({ bubbles, bubbleFor, leaderboard, feed, onJump
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="replay-indicator">
|
<div className="replay-indicator">
|
||||||
<span className="replay-status">{isPaused ? 'PAUSED' : 'REPLAY MODE'}</span>
|
<span className="replay-status">{isPaused ? '已暂停' : '回放模式'}</span>
|
||||||
<button
|
<button
|
||||||
className="replay-button"
|
className="replay-button"
|
||||||
onClick={isPaused ? resumeReplay : pauseReplay}
|
onClick={isPaused ? resumeReplay : pauseReplay}
|
||||||
|
|||||||
@@ -181,17 +181,17 @@ export default function RulesView() {
|
|||||||
},
|
},
|
||||||
zh: {
|
zh: {
|
||||||
section1Title: "Agent 设定",
|
section1Title: "Agent 设定",
|
||||||
pmRole: "Portfolio Manager",
|
pmRole: "投资经理",
|
||||||
pmDesc: "负责最终交易决策和团队协作",
|
pmDesc: "负责最终交易决策和团队协作",
|
||||||
rmRole: "Risk Manager",
|
rmRole: "风控经理",
|
||||||
rmDesc: "监控组合风险并执行风险限制",
|
rmDesc: "监控组合风险并执行风险限制",
|
||||||
analystsRole: "Analysts",
|
analystsRole: "分析师",
|
||||||
analystsDesc: "使用不同工具和 AI 模型进行专业研究:",
|
analystsDesc: "使用不同工具和 AI 模型进行专业研究:",
|
||||||
analysts: [
|
analysts: [
|
||||||
{ name: "Valuation Analyst", model: "Moonshot", modelKey: "Moonshot" },
|
{ name: "估值分析师", model: "Moonshot", modelKey: "Moonshot" },
|
||||||
{ name: "Sentiment Analyst", model: "Qwen", modelKey: "Alibaba" },
|
{ name: "情绪分析师", model: "Qwen", modelKey: "Alibaba" },
|
||||||
{ name: "Fundamentals Analyst", model: "DeepSeek", modelKey: "DeepSeek" },
|
{ name: "基本面分析师", model: "DeepSeek", modelKey: "DeepSeek" },
|
||||||
{ name: "Technical Analyst", model: "Zhipu AI", modelKey: "Zhipu AI" }
|
{ name: "技术分析师", model: "Zhipu AI", modelKey: "Zhipu AI" }
|
||||||
],
|
],
|
||||||
|
|
||||||
section2Title: "Agent 决策机制",
|
section2Title: "Agent 决策机制",
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
color: '#000000',
|
color: '#000000',
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Performance
|
业绩表现
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Total Asset Value
|
总资产价值
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 36,
|
fontSize: 36,
|
||||||
@@ -202,7 +202,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Excess Return
|
超额收益
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
@@ -218,7 +218,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
fontFamily: '"Courier New", monospace'
|
fontFamily: '"Courier New", monospace'
|
||||||
}}>
|
}}>
|
||||||
vs. VW: {excessReturnData.benchmarkReturn >= 0 ? '+' : ''}{excessReturnData.benchmarkReturn.toFixed(2)}%
|
vs 市值加权: {excessReturnData.benchmarkReturn >= 0 ? '+' : ''}{excessReturnData.benchmarkReturn.toFixed(2)}%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -233,7 +233,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Win Rate
|
胜率
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
@@ -252,7 +252,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
fontFamily: '"Courier New", monospace'
|
fontFamily: '"Courier New", monospace'
|
||||||
}}>
|
}}>
|
||||||
{pmWinRateData.totalWins}Win / {pmWinRateData.evaluatedTotal}Eval
|
{pmWinRateData.totalWins}胜 / {pmWinRateData.evaluatedTotal}评
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -267,7 +267,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Absolute Return
|
绝对收益
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
@@ -296,7 +296,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Cash Position
|
现金头寸
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@@ -322,7 +322,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Total Trades
|
总交易数
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@@ -350,7 +350,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
color: '#666666'
|
color: '#666666'
|
||||||
}}>
|
}}>
|
||||||
Portfolio Weights
|
组合权重
|
||||||
</div>
|
</div>
|
||||||
<div className="statistics-table-container" style={{
|
<div className="statistics-table-container" style={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
@@ -397,7 +397,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
letterSpacing: 0.5
|
letterSpacing: 0.5
|
||||||
}}>
|
}}>
|
||||||
No statistics available
|
暂无统计数据
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -433,7 +433,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
color: '#000000',
|
color: '#000000',
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Portfolio Holdings
|
持仓明细
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -448,7 +448,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
letterSpacing: 0.5
|
letterSpacing: 0.5
|
||||||
}}>
|
}}>
|
||||||
No positions currently held
|
当前无持仓
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -456,11 +456,11 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
<table className="data-table">
|
<table className="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Ticker</th>
|
<th>代码</th>
|
||||||
<th>Quantity</th>
|
<th>数量</th>
|
||||||
<th>Price</th>
|
<th>价格</th>
|
||||||
<th>Value</th>
|
<th>市值</th>
|
||||||
<th>Weight</th>
|
<th>权重</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -505,7 +505,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
onClick={() => setHoldingsPage(p => Math.max(1, p - 1))}
|
onClick={() => setHoldingsPage(p => Math.max(1, p - 1))}
|
||||||
disabled={holdingsPage === 1}
|
disabled={holdingsPage === 1}
|
||||||
>
|
>
|
||||||
◀ Prev
|
◀ 上一页
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="pagination-info">
|
<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))}
|
onClick={() => setHoldingsPage(p => Math.min(totalHoldingsPages, p + 1))}
|
||||||
disabled={holdingsPage === totalHoldingsPages}
|
disabled={holdingsPage === totalHoldingsPages}
|
||||||
>
|
>
|
||||||
Next ▶
|
下一页 ▶
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -552,7 +552,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
color: '#000000',
|
color: '#000000',
|
||||||
textTransform: 'uppercase'
|
textTransform: 'uppercase'
|
||||||
}}>
|
}}>
|
||||||
Transaction History
|
交易历史
|
||||||
</h2>
|
</h2>
|
||||||
{trades.length > 0 && (
|
{trades.length > 0 && (
|
||||||
<div style={{
|
<div style={{
|
||||||
@@ -560,7 +560,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
color: '#666666',
|
color: '#666666',
|
||||||
fontFamily: '"Courier New", monospace'
|
fontFamily: '"Courier New", monospace'
|
||||||
}}>
|
}}>
|
||||||
{trades.length} total
|
共 {trades.length} 笔
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -576,7 +576,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
letterSpacing: 0.5
|
letterSpacing: 0.5
|
||||||
}}>
|
}}>
|
||||||
No trades recorded
|
暂无交易记录
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -584,11 +584,11 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
<table className="data-table">
|
<table className="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Time</th>
|
<th>时间</th>
|
||||||
<th>Stock</th>
|
<th>股票</th>
|
||||||
<th>Side</th>
|
<th>方向</th>
|
||||||
<th>Qty</th>
|
<th>数量</th>
|
||||||
<th>Price</th>
|
<th>价格</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -638,7 +638,7 @@ export default function StatisticsView({ trades, holdings, stats, baseline_vw, e
|
|||||||
onClick={() => setTradesPage(p => Math.max(1, p - 1))}
|
onClick={() => setTradesPage(p => Math.max(1, p - 1))}
|
||||||
disabled={tradesPage === 1}
|
disabled={tradesPage === 1}
|
||||||
>
|
>
|
||||||
◀ Prev
|
◀ 上一页
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="pagination-info">
|
<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))}
|
onClick={() => setTradesPage(p => Math.min(totalTradesPages, p + 1))}
|
||||||
disabled={tradesPage === totalTradesPages}
|
disabled={tradesPage === totalTradesPages}
|
||||||
>
|
>
|
||||||
Next ▶
|
下一页 ▶
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -66,43 +66,43 @@ export const AGENT_SEATS = [
|
|||||||
export const AGENTS = [
|
export const AGENTS = [
|
||||||
{
|
{
|
||||||
id: "portfolio_manager",
|
id: "portfolio_manager",
|
||||||
name: "Portfolio Manager",
|
name: "投资经理",
|
||||||
role: "Portfolio Manager",
|
role: "投资经理",
|
||||||
avatar: CDN_ASSETS.companyRoom.agent_1,
|
avatar: CDN_ASSETS.companyRoom.agent_1,
|
||||||
colors: { bg: "#F9FDFF", text: "#1565C0", accent: "#1565C0" }
|
colors: { bg: "#F9FDFF", text: "#1565C0", accent: "#1565C0" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "risk_manager",
|
id: "risk_manager",
|
||||||
name: "Risk Manager",
|
name: "风控经理",
|
||||||
role: "Risk Manager",
|
role: "风控经理",
|
||||||
avatar: CDN_ASSETS.companyRoom.agent_2,
|
avatar: CDN_ASSETS.companyRoom.agent_2,
|
||||||
colors: { bg: "#FFF8F8", text: "#C62828", accent: "#C62828" }
|
colors: { bg: "#FFF8F8", text: "#C62828", accent: "#C62828" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "valuation_analyst",
|
id: "valuation_analyst",
|
||||||
name: "Valuation Analyst",
|
name: "估值分析师",
|
||||||
role: "Valuation Analyst",
|
role: "估值分析师",
|
||||||
avatar: CDN_ASSETS.companyRoom.agent_3,
|
avatar: CDN_ASSETS.companyRoom.agent_3,
|
||||||
colors: { bg: "#FAFFFA", text: "#2E7D32", accent: "#2E7D32" }
|
colors: { bg: "#FAFFFA", text: "#2E7D32", accent: "#2E7D32" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "sentiment_analyst",
|
id: "sentiment_analyst",
|
||||||
name: "Sentiment Analyst",
|
name: "情绪分析师",
|
||||||
role: "Sentiment Analyst",
|
role: "情绪分析师",
|
||||||
avatar: CDN_ASSETS.companyRoom.agent_4,
|
avatar: CDN_ASSETS.companyRoom.agent_4,
|
||||||
colors: { bg: "#FCFAFF", text: "#6A1B9A", accent: "#6A1B9A" }
|
colors: { bg: "#FCFAFF", text: "#6A1B9A", accent: "#6A1B9A" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "fundamentals_analyst",
|
id: "fundamentals_analyst",
|
||||||
name: "Fundamentals Analyst",
|
name: "基本面分析师",
|
||||||
role: "Fundamentals Analyst",
|
role: "基本面分析师",
|
||||||
avatar: CDN_ASSETS.companyRoom.agent_5,
|
avatar: CDN_ASSETS.companyRoom.agent_5,
|
||||||
colors: { bg: "#FFFCF7", text: "#E65100", accent: "#E65100" }
|
colors: { bg: "#FFFCF7", text: "#E65100", accent: "#E65100" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "technical_analyst",
|
id: "technical_analyst",
|
||||||
name: "Technical Analyst",
|
name: "技术分析师",
|
||||||
role: "Technical Analyst",
|
role: "技术分析师",
|
||||||
avatar: CDN_ASSETS.companyRoom.agent_6,
|
avatar: CDN_ASSETS.companyRoom.agent_6,
|
||||||
colors: { bg: "#F9FEFF", text: "#00838F", accent: "#00838F" }
|
colors: { bg: "#F9FEFF", text: "#00838F", accent: "#00838F" }
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user