TradingAgents-CN 用户偏好设置与财务指标计算优化实战:TTM 计算、WebSocket 连接与 UI 改进全解析
【免费下载链接】TradingAgents-CN基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN
导读:本文基于 TradingAgents-CN 官方技术博客(2025-10-26)展开,系统梳理了本次 36 个提交所涵盖的用户偏好设置系统重构、TTM 财务指标计算优化、Tushare Token 配置优先级修复、Docker 环境下 WebSocket 连接修复以及 UI 体验改进五大主题。读完本文,你将掌握用户偏好前后端同步机制、正确的 TTM 计算公式与累计值处理逻辑、数据源配置的优先级策略,以及开发/生产环境统一的 WebSocket 连接方案,可直接用于排查本项目中的同类问题。
📋 概述
2025 年 10 月 26 日,TradingAgents-CN 完成了一次全面的系统优化工作。通过36 个提交,完成了用户偏好设置系统重构、财务指标计算优化、WebSocket 连接修复、UI 体验改进等多项工作,显著提升了系统的数据准确性、用户体验和稳定性。统计口径如下:
- 总提交数:36 个
- 修改文件数:120+ 个
- 新增代码:约 4,000 行
- 删除代码:约 1,500 行
- 净增代码:约 2,500 行
功能分类上,用户偏好设置共 10 项修复、财务指标计算 12 项优化、WebSocket 连接 2 项修复、UI 体验改进 5 项优化,并新增 7 篇配套文档。
🎯 核心改进一:用户偏好设置系统重构
1.1 修复所有设置保存问题
涉及提交:41ca79f、6283a5c、e2fef6b、e56c571
问题背景:用户在前端修改个人设置后,刷新页面设置会恢复到原值:
- ❌ 主题设置(深色/浅色)不生效
- ❌ 分析偏好设置(模型、分析师)不生效
- ❌ 通知设置不生效
- ❌ 邮箱地址不生效
根本原因有三层:
- 前端保存到 localStorage,后端保存到数据库:前端使用
localStorage存储设置,后端使用 MongoDBusers集合存储,两者不同步; - 页面刷新时优先读取后端数据:
authStore初始化时从后端/api/auth/me获取用户信息,覆盖了localStorage中的设置; - 后端未正确保存用户偏好:
/api/auth/me接口未返回preferences字段,用户偏好设置未持久化到数据库。
解决方案分三步走。
步骤 1:后端返回用户偏好设置。在 app/routers/auth_db.py 的/me接口中新增preferences字段:
# app/routers/auth_db.py @router.get("/me") async def get_current_user(current_user: dict = Depends(get_current_user_from_db)): """获取当前用户信息""" return { "id": str(user.id), "username": user.username, "email": user.email, "name": user.username, "is_admin": user.is_admin, "roles": ["admin"] if user.is_admin else ["user"], "preferences": user.preferences.model_dump() if user.preferences else {} # ← 新增 }在仓库中,该实现已落地为 app/routers/auth_db.py 的"preferences": user.preferences.model_dump() if user.preferences else {}。同时在更新用户接口中(约 app/routers/auth_db.py),对preferences采用了浅合并(merge)策略:merged_prefs = {**current_prefs, **payload["preferences"]},再通过UserPreferences(**merged_prefs)构造模型,保证部分更新不会覆盖用户其它偏好字段。
步骤 2:前端同步用户偏好到 appStore:
// frontend/src/stores/auth.ts setAuthInfo(token: string, refreshToken: string, user: User) { this.token = token this.refreshToken = refreshToken this.user = user // 同步用户偏好设置到 appStore this.syncUserPreferencesToAppStore() } syncUserPreferencesToAppStore() { const appStore = useAppStore() if (this.user?.preferences) { // 同步主题设置 if (this.user.preferences.theme) { appStore.theme = this.user.preferences.theme appStore.applyTheme() } // 同步分析偏好 if (this.user.preferences.analysis) { appStore.analysisPreferences = this.user.preferences.analysis } // 同步通知设置 if (this.user.preferences.notifications) { appStore.notificationSettings = this.user.preferences.notifications } } }步骤 3:添加用户偏好设置迁移脚本,为存量用户补齐默认偏好:
# scripts/migrate_user_preferences.py async def migrate_user_preferences(): """迁移用户偏好设置到数据库""" db = get_database() users_collection = db[settings.USERS_COLLECTION] # 查找所有用户 users = await users_collection.find({}).to_list(None) for user in users: # 如果用户没有 preferences 字段,添加默认值 if "preferences" not in user or not user["preferences"]: default_preferences = { "theme": "light", "analysis": { "default_model": "gpt-4o-mini", "default_analysts": ["market", "fundamentals", "news", "social"] }, "notifications": { "email_enabled": False, "browser_enabled": True } } await users_collection.update_one( {"_id": user["_id"]}, {"$set": {"preferences": default_preferences}} )效果:✅ 用户设置保存到数据库;✅ 刷新页面设置不丢失;✅ 前后端数据同步;✅ 支持多设备同步。
源码佐证:当前仓库中用户偏好模型已成型,见 app/models/user.py 的UserPreferencesPydantic 模型,包含分析偏好(default_market、default_depth1-5 级、default_analysts、auto_refresh、refresh_interval)、外观(ui_theme、sidebar_width)、语言(language)与通知(notifications_enabled、email_notifications、desktop_notifications等)四类字段,且 app/models/user.py 中preferences: UserPreferences = Field(default_factory=UserPreferences)已内嵌于User模型,从数据层保证了偏好持久化能力。
1.2 优化分析偏好设置
涉及提交:767ac03、25de33c
问题背景:
- 默认值不一致:个人设置页面默认值为
gpt-4o-mini,而单股分析页面默认值为gpt-4o,导致用户困惑; - 分析页面不读取用户偏好:每次打开分析页面都使用硬编码的默认值,用户需要重新选择模型和分析师。
解决方案:在分析页面onMounted阶段优先读取用户偏好:
// frontend/src/views/Analysis/SingleStock.vue onMounted(async () => { // 优先读取用户偏好设置 const appStore = useAppStore() if (appStore.analysisPreferences) { analysisForm.model = appStore.analysisPreferences.default_model || 'gpt-4o-mini' analysisForm.analysts = appStore.analysisPreferences.default_analysts || ['market', 'fundamentals', 'news', 'social'] } })效果:✅ 默认值统一为gpt-4o-mini;✅ 分析页面自动读取用户偏好;✅ 提高用户体验。
说明:
gpt-4o-mini是该次更新时的默认模型。实际项目中 LLM 默认值可通过配置文件与 Web 后台的模型目录动态调整,请以当前部署版本的配置为准。
🎯 核心改进二:财务指标计算优化
2.1 修复 TTM(Trailing Twelve Months)计算问题
涉及提交:9c11d98、5de898e、b0413c6、5384339、8077316
问题背景:TTM 是计算动态市盈率(PE_TTM)和市销率(PS_TTM)的关键指标,但原有计算存在三个严重问题:
- 累计值处理错误:A 股财报数据是年初至今的累计值(如 Q3 报表 = 前三季度累计),直接相加会重复计算。例如
Q1 + Q2 + Q3 = 前三季度 × 2(错误); - 基准期选择不当:使用 Q4 作为基准期,但 Q4(年报)数据通常延迟发布,导致 TTM 数据不及时;
- 简单年化策略不准确:当没有完整 4 个季度数据时,简单年化(如
Q1 × 4)忽略了季节性因素,导致估值指标严重失真。
正确的 TTM 计算公式:
TTM = 最新年报 + (最新季报 - 去年同期季报)示例(假设当前为 2024-10-26,最新财报为 2024Q3):
TTM_营业收入 = 2023年报营业收入 + (2024Q3营业收入 - 2023Q3营业收入) TTM_净利润 = 2023年报净利润 + (2024Q3净利润 - 2023Q3净利润)实现代码(重构后的 TTM 计算逻辑):
# tradingagents/data_sources/tushare_adapter.py def _calculate_ttm_metrics(self, reports: List[Dict]) -> Optional[Dict]: """计算TTM指标(正确处理累计值)""" # 1. 找到最新年报 annual_reports = [r for r in reports if r["report_type"] == "年报"] if not annual_reports: return None latest_annual = annual_reports[0] # 2. 找到最新季报 quarterly_reports = [r for r in reports if r["report_type"] in ["一季报", "中报", "三季报"]] if not quarterly_reports: # 如果没有季报,直接使用年报数据 return { "revenue_ttm": latest_annual.get("revenue"), "net_profit_ttm": latest_annual.get("net_profit") } latest_quarterly = quarterly_reports[0] # 3. 找到去年同期季报 latest_quarter = latest_quarterly["report_type"] latest_year = int(latest_quarterly["end_date"][:4]) last_year = latest_year - 1 last_year_same_quarter = None for report in reports: if (report["report_type"] == latest_quarter and int(report["end_date"][:4]) == last_year): last_year_same_quarter = report break if not last_year_same_quarter: # 如果没有去年同期数据,使用年报数据 return { "revenue_ttm": latest_annual.get("revenue"), "net_profit_ttm": latest_annual.get("net_profit") } # 4. 计算 TTM revenue_ttm = ( latest_annual.get("revenue", 0) + latest_quarterly.get("revenue", 0) - last_year_same_quarter.get("revenue", 0) ) net_profit_ttm = ( latest_annual.get("net_profit", 0) + latest_quarterly.get("net_profit", 0) - last_year_same_quarter.get("net_profit", 0) ) return { "revenue_ttm": revenue_ttm if revenue_ttm > 0 else None, "net_profit_ttm": net_profit_ttm if net_profit_ttm != 0 else None }仓库中的最新实现:当前源码已将 TTM 计算演进为_calculate_ttm_from_tushare(见 tradingagents/dataflows/providers/china/tushare.py),核心逻辑与上文一致但更严谨,可视为本次重构的正式落地版本:
- 报告期语义:Tushare 利润表数据均为累计值——
2025Q1 (20250331)为 1-3 月累计、2025Q2 (20250630)为 1-6 月累计、2025Q3 (20250930)为 1-9 月累计、2025Q4 (20251231)为年报; - 基准期选择升级:不再简单取"最新年报",而是查找去年同期之后的最近年报作为基准期(代码注释明确"例如:如果最新期是 2025Q2,去年同期是 2024Q2,则查找 2024 年报(20241231)"),避免 Q4 年报延迟发布导致的 TTM 不及时问题;
- 缺数据即返回 None:缺少去年同期数据、去年同期值为空、找不到基准年报、基准年报值为空四种情况下均记录 warning 日志并返回
None,绝不降级为简单年化——这正是对提交5de898e(移除简单年化降级策略)的实现保证; - 最新期若为年报直接使用:当
end_date末四位为1231时直接返回年报值,无需再做滚动计算。
计算过程还带有完整调试日志,便于核验:
✅ TTM计算: 20241231(基准年报值) + (20250630(本期累计) - 20240630(去年同期累计)) = TTM值效果:✅ TTM 计算准确;✅ 正确处理累计值;✅ 基准期选择合理;✅ 移除不准确的年化策略。
2.2 修复市销率(PS)计算问题
涉及提交:f333020、c522523、ad69c71
问题背景:市销率(PS)此前使用季度或半年报数据计算,导致严重失真:
错误计算:PS = 市值 / Q3营业收入(前三季度累计) 正确计算:PS = 市值 / TTM营业收入(最近12个月)示例:某股票市值 100 亿,2024Q3 营业收入(累计)60 亿,TTM 营业收入 80 亿:
错误 PS = 100 / 60 = 1.67 正确 PS = 100 / 80 = 1.25解决方案:估值指标统一改用 TTM 口径:
# tradingagents/data_sources/tushare_adapter.py def get_fundamental_data(self, code: str) -> Dict: """获取基本面数据""" # 1. 获取财报数据 reports = self._get_financial_reports(code) # 2. 计算 TTM 指标 ttm_metrics = self._calculate_ttm_metrics(reports) # 3. 获取实时股价和市值 quote = self.get_realtime_quote(code) market_cap = quote.get("market_cap") # 总市值(亿元) # 4. 计算估值指标 if ttm_metrics and market_cap: # 市销率 = 市值 / TTM营业收入 ps = market_cap / ttm_metrics["revenue_ttm"] if ttm_metrics["revenue_ttm"] else None # 市盈率 = 市值 / TTM净利润 pe_ttm = market_cap / ttm_metrics["net_profit_ttm"] if ttm_metrics["net_profit_ttm"] else None return { "ps": ps, "pe_ttm": pe_ttm, # ... 其他指标 }仓库源码中,TTM 指标已进入真实基本面数据结构:在 tradingagents/dataflows/providers/china/tushare.py 中,revenue_ttm(营业收入 TTM)与net_profit_ttm(归属母公司净利润 TTM)作为独立字段返回,供上层 PE_TTM / PS 估值计算使用;同时 tradingagents/dataflows/providers/china/tushare.py 的实时行情接口也直接拉取 Tushare 官方计算的pe_ttm字段用于交叉验证。
效果:✅ PS 计算准确;✅ 使用 TTM 营业收入;✅ 避免季节性失真。
2.3 修复 Tushare Token 配置优先级问题
涉及提交:75edbc8、da3406b
问题背景:用户在 Web 后台修改 Tushare Token 后,系统仍使用环境变量中的旧 Token:
- 配置优先级不合理:环境变量优先级高于数据库配置,用户在 Web 后台修改无效;
- 异步/同步冲突:配置读取使用异步方法,但部分代码在同步上下文中调用,导致配置读取失败。
解决方案:
步骤 1:调整配置优先级(数据库优先):
# app/services/config_service.py async def get_data_source_config(self, source_name: str) -> Optional[Dict]: """获取数据源配置(数据库优先)""" # 1. 优先从数据库读取 db_config = await self._get_from_database(source_name) if db_config and db_config.get("api_key"): return db_config # 2. 降级使用环境变量 env_key = f"{source_name.upper()}_TOKEN" env_value = os.getenv(env_key) if env_value: return {"api_key": env_value} return None步骤 2:修复异步/同步冲突(分阶段初始化):
# tradingagents/data_sources/tushare_adapter.py class TushareAdapter: def __init__(self): # 同步初始化,使用环境变量 self.token = os.getenv("TUSHARE_TOKEN") self._provider = None async def initialize(self): """异步初始化,从数据库读取配置""" config_service = ConfigService() config = await config_service.get_data_source_config("tushare") if config and config.get("api_key"): self.token = config["api_key"] # 初始化 provider if self.token: self._provider = ts.pro_api(self.token)仓库佐证:当前 app/services/config_service.py 中数据源配置的管理已全面围绕data_source_configs展开(含 config_service.py 的优先级判断注释、config_service.py 同步更新system_configs集合的逻辑),并且测试数据源配置时存在"数据库截断 Key 与完整 Key 回填"机制(见 config_service.py):Web 后台保存的 Key 可能以截断形式展示,测试时若检测到截断则从数据库回填完整 Key,数据库无有效 Key 时才降级尝试环境变量——与"数据库优先"的策略一脉相承。
效果:✅ Web 后台修改立即生效;✅ 数据库配置优先级高于环境变量;✅ 修复异步/同步冲突。
🎯 核心改进三:WebSocket 连接优化
3.1 修复 Docker 部署时 WebSocket 连接失败
涉及提交:d0512fc、f176a10
问题背景:Docker 部署时,前端尝试连接硬编码的ws://localhost:8000,而实际应连接服务器的真实地址,导致通知 WebSocket 连接失败。
解决方案:
步骤 1:启用 Vite WebSocket 代理(开发环境):
// frontend/vite.config.ts export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true, secure: false, ws: true // 🔥 启用 WebSocket 代理支持 } } } })步骤 2:简化连接逻辑,统一使用当前访问地址:
// frontend/src/stores/notifications.ts const connectWebSocket = () => { // 统一使用当前访问的服务器地址 const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:' const host = window.location.host const wsUrl = `${wsProtocol}//${host}/api/ws/notifications?token=${token}` ws = new WebSocket(wsUrl) }该逻辑在仓库中已完整落地于 frontend/src/stores/notifications.ts,且 token 使用encodeURIComponent(token)进行 URL 编码,并具备断线后自动重连(connectWebSocket()在断开事件中被再次调用)与登出时disconnectWebSocket()清理连接的完整生命周期管理。
工作原理:
| 环境 | 访问地址 | WebSocket 连接 | 代理路径 |
|---|---|---|---|
| 开发 | http://localhost:3000 | ws://localhost:3000/api/ws/... | Vite 代理到ws://localhost:8000/api/ws/... |
| 生产 | http://服务器IP | ws://服务器IP/api/ws/... | Nginx 代理到ws://backend:8000/api/ws/... |
| HTTPS | https://域名 | wss://域名/api/ws/... | Nginx 代理到ws://backend:8000/api/ws/... |
效果:✅ 无需修改代码;✅ 自动协议适配(HTTP→WS,HTTPS→WSS);✅ 自动地址适配;✅ 开发和生产环境统一。
🎯 核心改进四:UI 体验改进
4.1 添加数据源注册引导功能
涉及提交:f7e4546、0ad8489、9a57973、d58484e
功能概述:在数据源配置页面(Provider 对话框)添加注册引导,帮助用户快速获取 API Key:
<!-- frontend/src/views/Settings/components/ProviderDialog.vue --> <el-alert v-if="!form.api_key && providerInfo.register_url" type="info" :closable="false" style="margin-bottom: 16px;" > <template #title> <div style="display: flex; align-items: center; gap: 8px;"> <el-icon><InfoFilled /></el-icon> <span>还没有 API Key?</span> <el-link :href="providerInfo.register_url" target="_blank" type="primary" :underline="false" > 点击注册 {{ providerInfo.display_name }} <el-icon><Right /></el-icon> </el-link> </div> </template> </el-alert>触发条件为!form.api_key && providerInfo.register_url:仅当用户尚未填写 API Key 且该数据源提供了官方注册地址时才展示引导,避免干扰已配置用户。同时配套提交d58484e补充了providerInfo的 TypeScript 类型定义,修复类型错误。
效果:✅ 用户可快速跳转到注册页面;✅ 提高新用户上手速度;✅ 减少配置错误。
4.2 修复深色主题下的白色背景问题
涉及提交:f1fe1d0
问题背景:深色主题下,部分页面仍显示白色背景,对比度不足。
解决方案:在深色主题样式表中统一覆盖背景色,回归到 Element Plus 的 CSS 变量体系(--el-bg-color、--el-text-color-primary),保证主题切换时各组件自动联动:
// frontend/src/styles/dark-theme.scss html.dark { // 页面背景 .page-container { background-color: var(--el-bg-color) !important; } // 卡片背景 .el-card { background-color: var(--el-bg-color) !important; color: var(--el-text-color-primary) !important; } // 表单背景 .el-form { background-color: transparent !important; } }效果:✅ 深色主题下背景统一;✅ 提高对比度;✅ 改善用户体验。
4.3 在关于页面添加原项目介绍和致谢
涉及提交:70b1971
功能概述:在"关于"页面新增致谢卡片,说明项目源自开源项目 TradingAgents(作者 virattt),并中文化与功能增强:
<el-card> <template #header> <div class="card-header"> <span>🙏 致谢</span> </div> </template> <el-descriptions :column="1" border> <el-descriptions-item label="原项目"> <el-link href="https://github.com/virattt/trading-agents" target="_blank"> TradingAgents by virattt </el-link> </el-descriptions-item> <el-descriptions-item label="说明"> 本项目基于 TradingAgents 进行中文化和功能增强,感谢原作者的开源贡献! </el-descriptions-item> </el-descriptions> </el-card>效果:✅ 尊重原作者贡献;✅ 说明项目来源;✅ 提高项目透明度。
🔧 技术亮点汇总
1. TTM 计算公式
正确处理累计值,避免重复计算:
TTM = 最新年报 + (最新季报 - 去年同期季报)核心要点:财报累计值不可直接相加;基准年报应选"去年同期之后的最近年报"而非 Q4;缺失数据时返回None而非简单年化,杜绝估值失真。
2. 配置优先级策略
数据库配置优先于环境变量,支持 Web 后台修改立即生效:
# 1. 优先从数据库读取 db_config = await self._get_from_database(source_name) if db_config and db_config.get("api_key"): return db_config # 2. 降级使用环境变量 env_value = os.getenv(f"{source_name.upper()}_TOKEN")3. WebSocket 自动适配
统一开发和生产环境,无需修改代码:
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:' const host = window.location.host const wsUrl = `${wsProtocol}//${host}/api/ws/notifications?token=${token}`4. 用户偏好同步机制
前后端数据同步,支持多设备:
syncUserPreferencesToAppStore() { const appStore = useAppStore() if (this.user?.preferences) { appStore.theme = this.user.preferences.theme appStore.analysisPreferences = this.user.preferences.analysis appStore.notificationSettings = this.user.preferences.notifications } }🚀 升级指南
步骤 1:拉取最新代码
git pull origin v1.0.0-preview步骤 2:运行用户偏好设置迁移脚本
.\.venv\Scripts\python scripts/migrate_user_preferences.pyLinux / macOS 环境将
.\.venv\Scripts\python替换为.venv/bin/python即可。该脚本会为preferences字段缺失或为空的存量用户写入默认偏好。
步骤 3:重启服务
# Docker 环境 docker-compose -f docker-compose.hub.nginx.yml pull docker-compose -f docker-compose.hub.nginx.yml up -d # 本地开发环境 .\.venv\Scripts\python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000Docker 部署说明:生产环境建议使用仓库根目录的 docker-compose.yml(或带 Nginx 的 docker-compose.hub.nginx.yml)编排前后端与数据库服务,Nginx 负责将/api(含 WebSocket 升级请求)反代到后端容器。
步骤 4:验证
测试用户偏好设置
- 修改主题设置,刷新页面验证是否生效
- 修改分析偏好,打开分析页面验证是否自动应用
测试财务指标计算
- 查看基本面分析页面
- 验证 PE_TTM、PS 等指标是否准确(可对比 Tushare 官方
pe_ttm字段)
测试 WebSocket 连接
- 打开浏览器控制台
- 查看是否有 WebSocket 连接成功的日志
📖 新增文档
本次更新同步产出了 7 篇配套文档,供进一步深入查阅:
docs/fixes/user-preferences-fix.md- 用户偏好设置修复文档docs/fixes/ttm-calculation-fix.md- TTM 计算问题修复总结docs/fixes/async-sync-conflict-fix.md- 异步/同步冲突问题修复docs/fixes/financial-metrics-audit.md- 估算财务指标审计总结docs/configuration/tushare-token-priority.md- Tushare Token 配置优先级说明docs/configuration/websocket-connection.md- WebSocket 连接配置指南docs/features/data-source-registration-guide.md- 数据源注册引导功能说明
🎉 总结
今日成果
- ✅36 次提交,120+ 个文件修改,4,000+ 行新增代码,1,500+ 行删除代码
核心价值
- 用户体验显著提升:设置保存不丢失、分析页面自动应用偏好、深色主题体验优化、数据源注册引导降低上手门槛;
- 数据准确性大幅提高:TTM 计算准确、PS/PE 指标可靠、财务数据质量提升,从根源上消除累计值重复计算与季节性失真;
- 系统稳定性增强:WebSocket 连接稳定(开发/生产环境统一)、配置管理优化(数据库优先)、异步/同步冲突修复;
- 开发体验改善:统一开发和生产环境、配置优先级合理、代码质量提升。
如果你正在部署或二次开发 TradingAgents-CN,建议优先核对preferences字段是否随/api/auth/me返回、TTM 是否采用"基准年报 + (本期累计 − 去年同期累计)"公式、以及 WebSocket 地址是否基于window.location动态生成——这三处正是本次优化中最容易出现回退或配置遗漏的关键点。
【免费下载链接】TradingAgents-CN基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考