Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

481 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TradingAgents 股票分析工作流程详解
> **文档时间**: 2026-05-05
> **相关文档**: [技术架构分析](./architecture-and-modifications.md)
> **配套图表**: [流程图](workflow-pipeline.html) | [架构图](workflow-architecture.html)
---
## 一、一次分析的完整生命周期
当用户在 Web UI 输入 `600362` 并点击"分析",系统会经历以下完整流程:
```
用户点击 [分析] → 后台异步任务 → 约 3-8 分钟后 → 输出交易决策
```
整个流程由 **LangGraph StateGraph** 驱动,状态在 15+ 个节点间流转。下面按执行顺序逐步拆解。
---
## 二、流程图
![流程图](workflow-pipeline.html)
> 详细架构图: [workflow-architecture.html](workflow-architecture.html)
---
## 三、分阶段详解
### 阶段 1:请求接收与任务创建
**入口**: `web_server.py` → `POST /api/analyze`
```
用户输入: { ticker: "600362", date: "2026-04-30", analysts: [...], max_debate_rounds: 1 }
```
1. 生成唯一的 `task_id`(格式: `600362_20260505191807`)
2. 创建任务记录写入 `tasks_store`(内存字典)
3. 通过 FastAPI `BackgroundTasks` 异步启动 `run_analysis_task()`
4. 立即返回 `{"task_id": "...", "status": "pending"}`
5. 前端每 3 秒轮询 `GET /api/task/{task_id}` 获取进度
### 阶段 2:Graph 初始化
**文件**: `tradingagents/graph/trading_graph.py` → `TradingAgentsGraph.propagate()`
```python
# 1. 读取配置
config = get_config() # 默认 vendor: akshare
llm_name = config["llm_provider"] # DeepSeek
# 2. 初始化 LangGraph StateGraph
workflow = StateGraph(AgentState)
# 3. 按用户选择的分析师类型动态添加节点
# 可选: market, sentiment, news, fundamentals
# 默认: market + news
```
`AgentState` 中关键字段:
| 字段 | 初始值 | 说明 |
|------|--------|------|
| `messages` | `[]` | 全流程消息日志 |
| `ticker` | `"600362"` | 股票代码 |
| `trade_date` | `"2026-04-30"` | 分析日期 |
| `company_name` | `""` | 公司名称(初始为空) |
| `market_report` | `""` | 市场分析报告 |
| `sentiment_report` | `""` | 情绪分析报告 |
| `news_report` | `""` | 新闻分析报告 |
| `fundamentals_report` | `""` | 基本面报告 |
| `investment_plan` | `""` | 投资决策草案 |
| `final_decision` | `""` | 最终交易信号 |
| `current_debate_round` | `0` | 当前辩论轮次 |
### 阶段 3:并行分析师采集(核心环节)
这是整个流程中**数据量最大、耗时最长**的阶段。四个分析师节点通过 LangGraph 的并行边**同时启动**。
#### 3.1 市场分析师 (Market Analyst)
**文件**: `tradingagents/agents/analysts/market_analyst.py`
**角色定位**: 技术面专家
**System Prompt 核心**:
> "You are a trading assistant agent with extensive market insight. Your task is to analyze market data using technical indicators."
**执行的工具调用**:
```
1. get_stock_data("600362", "2026-03-30", "2026-04-30")
→ 路线: route_to_vendor → akshare_data.get_stock_data()
→ 输出: 最近一个月 OHLCV 数据(~22条日K线)
2. get_indicators("600362", "rsi", "2026-04-30", 30)
3. get_indicators("600362", "macd", "2026-04-30", 30)
4. get_indicators("600362", "bollinger_bands", "2026-04-30", 30)
5. get_indicators("600362", "volume", "2026-04-30", 30)
... (最多调用 19 项技术指标中的 5-8 项关键指标)
```
**指标清单**(akshare_data.py 支持的全部 19 项):
| 类别 | 指标 |
|------|------|
| 趋势类 | sma_20, ema_20, macd, adx |
| 动量类 | rsi, stochastic_oscillator, williams_r, cci |
| 波动类 | bollinger_bands, atr, standard_deviation |
| 成交量类 | obv, volume, volume_sma |
| 其他 | mfi, ppo, roc, triple_ema, vortex |
**输出**: 一份 800-1500 字的 Markdown 技术分析报告,含关键指标解读和趋势判断。
#### 3.2 社交媒体分析师 (Social Media Analyst)
**文件**: `tradingagents/agents/analysts/social_media_analyst.py`
**角色定位**: 市场情绪雷达
**执行的工具调用**:
```
1. get_news("600362", "2026-04-01", "2026-05-04")
→ 路线: route_to_vendor → akshare_news.get_news()
→ 数据源: 东方财富个股新闻
→ 输出: 最近 15 天内与该股相关的新闻(最多50条)
```
**输出**: 一份舆情分析报告,提炼市场情绪信号(恐慌/贪婪/中性)。
#### 3.3 新闻分析师 (News Analyst)
**文件**: `tradingagents/agents/analysts/news_analyst.py`
**角色定位**: 宏观政策雷达
**System Prompt 核心**:
> "You are a news analyst agent with expertise in global macro trends. Your task is to analyze news related to the company and the broader economic landscape."
**执行的工具调用**:
```
1. get_news("600362", "2026-04-01", "2026-05-04")
→ 与社媒分析师相同的数据源,但解读角度不同
2. get_global_news("2026-05-04", look_back_days=7, limit=20)
→ 路线: route_to_vendor → akshare_news.get_global_news()
→ 数据源: CCTV 新闻联播文字稿
→ 输出: 宏观政策、经济事件摘要
```
**输出**: 一份包含个股新闻分析和宏观经济背景的综合报告。
#### 3.4 基本面分析师 (Fundamentals Analyst)
**文件**: `tradingagents/agents/analysts/fundamentals_analyst.py`
**角色定位**: 价值投资者
**System Prompt 核心**:
> "You are a fundamental analysis expert with deep knowledge of valuation methodologies. Your task is to analyze company fundamentals and financial health."
**执行的工具调用** (共 4 次):
```
1. get_fundamentals("600362")
→ 数据: 公司概况、市盈率、市净率、总市值、流通股本等
2. get_balance_sheet("600362")
→ 数据: 最新一期资产负债表(总资产/负债/权益)
3. get_cashflow("600362")
→ 数据: 最新一期现金流量表(经营/投资/筹资现金流)
4. get_income_statement("600362")
→ 数据: 最新一期利润表(营收/成本/净利润/每股收益)
```
**输出**: 一份包含估值分析、财务健康度评分、盈利能力判断的基本面报告。
### 阶段 4:研究经理汇总
**文件**: `tradingagents/graph/trading_graph.py` → `create_research_manager_node()`
```
输入: 4份分析师报告(并行采集完成后的汇总)
处理: 研究经理 LLM 阅读所有报告,提取关键矛盾点
输出: 投资者简报 (investment briefing)
```
研究经理不产生独立决策,而是**整理和总结**分析师团队输出的核心信息,为下一阶段的辩论做准备。
### 阶段 5:多空辩论 (Debate)
**文件**: `tradingagents/graph/trading_graph.py` → `create_debate_node()` + `debate_bull_researcher()` + `debate_bear_researcher()`
这是框架的**核心创新**:用 LLM 模拟真实投资决策中的多空辩论。
#### 辩论角色
| 角色 | 文件 | 立场 | 职责 |
|------|------|------|------|
| **Bull Researcher** | `agents/researchers/bull_researcher.py` | 多头 | 找出所有支撑买入的论据 |
| **Bear Researcher** | `agents/researchers/bear_researcher.py` | 空头 | 找出所有支撑卖出的论据 |
| **Debate Judge** | `agents/managers/research_manager.py` | 裁判 | 评估双方论据,判断胜负 |
#### 辩论流程
```
轮次 1:
Bull → 提出 3-5 个多方论点(基于分析师报告)
Bear → 反驳多方论点 + 提出 3-5 个空方论点
Judge → 评估双方论据质量,判断本轮是否分出胜负
轮次 2 (如果裁判认为需要继续):
Bull → 回应空方反驳 + 补充新论点
Bear → 回应多方回应 + 强化空方论点
Judge → 再次评估
... 直到 max_debate_rounds 耗尽或裁判判定胜负
```
**关键**: `max_debate_rounds` 在配置中可调,默认 **1 轮**。每增加一轮,LLM 调用次数翻倍,总耗时显著增长。
#### 辩论输出
- 辩论完整记录(谁说了什么)
- 裁判的最终评估(多方胜/空方胜/平局)
### 阶段 6:交易员决策
**文件**: `tradingagents/agents/trader/trader_agent.py`
**System Prompt 核心**:
> "You are a professional trader agent. Based on the analysts' reports and debate results, make a trading decision."
**输入**:
- 4 份分析师报告
- 辩论摘要(裁判评估)
**处理**: 交易员 LLM 综合所有证据,形成投资决策草案
**输出**: `investment_plan` — 包含:
- 决策方向(Buy/Sell/Hold)
- 置信度评估
- 关键依据
- 风险提示
### 阶段 7:三级风控评审
**文件**: `tradingagents/graph/trading_graph.py` → `create_risk_analysis_node()` + 三个风控 agent
这是项目的**第二道安全阀**:交易员的决策必须通过风控审查。
| 风控角色 | 文件 | 风格 | 关注点 |
|----------|------|------|--------|
| **Aggressive** | `agents/risk_mgmt/aggressive_debator.py` | 激进 | 机会最大化,风险容忍度较高 |
| **Conservative** | `agents/risk_mgmt/conservative_debator.py` | 保守 | 本金安全第一,宁可错过不做错 |
| **Neutral** | `agents/risk_mgmt/neutral_debator.py` | 中立 | 平衡收益与风险 |
#### 流程
```
交易员输出投资草案
│
├→ Aggressive 评估 → "风险可接受,建议执行"
├→ Conservative 评估 → "风险过高,建议降低仓位"
└→ Neutral 评估 → "风险适中,可执行但需设置止损"
│
└→ Risk Judge 汇总三个评估 → 最终裁定
```
**Risk Judge** 文件: `tradingagents/agents/managers/risk_manager.py` — 综合三方意见,给出最终风控建议。
**注意**: 风控评审是一个循环 — 如果 `final_decision` 为空(未达成一致),图会回到交易员节点重新决策。
### 阶段 8:最终输出
经过以上所有阶段后,系统输出:
```python
{
"ticker": "600362",
"date": "2026-04-30",
"company_name": "江西铜业",
"market_report": "...", # 技术分析报告
"sentiment_report": "...", # 情绪分析报告
"news_report": "...", # 新闻分析报告
"fundamentals_report": "...", # 基本面报告
"investment_plan": "...", # 交易决策草案
"debate_log": "...", # 辩论记录
"final_decision": "BUY", # 最终交易信号: BUY / SELL / HOLD
"risk_assessment": "...", # 风控评估
"messages": [...] # 全流程消息日志
}
```
---
## 四、完整节点流转图(Graph 内部)
```
┌──────────────┐
│ START │
└──────┬───────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│mkt_start │ │snt_start │ │nws_start │ │fnd_start │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘
▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│mkt_analyst │ │snt_analyst │ │nws_analyst │ │fnd_analyst │
│(LLM分析) │ │(LLM分析) │ │(LLM分析) │ │(LLM分析) │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘
▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│mkt_tools │ │snt_tools │ │nws_tools │ │fnd_tools │
│(调用数据) │ │(调用数据) │ │(调用数据) │ │(调用数据) │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘
▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│mkt_cond │ │snt_cond │ │nws_cond │ │fnd_cond │
│(if 需继续) │ │(if 需继续) │ │(if 需继续) │ │(if 需继续) │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘
│ │ │ │
└────────────────┼────────────────┼────────────────┘
▼
┌──────────────┐
│ invest_judge │ (研究经理汇总)
└──────┬───────┘
│
┌───────────┴───────────┐
▼ ▼
┌────────────┐ ┌────────────┐
│bull_resrch │ │bear_resrch │
│(多方) │ │(空方) │
└─────┬──────┘ └─────┬──────┘
│ │
└──────────┬───────────┘
▼
┌──────────────┐
│ invest_judge │ (辩论裁判评估)
└──────┬───────┘
│ (if 辩论未结束 → 回到 bull/bear)
▼
┌──────────────┐
│ trader │ (交易员决策)
└──────┬───────┘
▼
┌──────────────┐
│trader_tools │ (可能需要查询数据)
└──────┬───────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────────┐ ┌──────────┐
│aggressive│ │conservative │ │ neutral │
│ 风控 │ │ 风控 │ │ 风控 │
└────┬─────┘ └──────┬───────┘ └────┬─────┘
│ │ │
└────────────────┼────────────────┘
▼
┌──────────────┐
│ risk_judge │ (风控终审)
└──────┬───────┘
│ (if 未通过 → 回到 trader)
▼
┌──────────────┐
│ final │ (输出结果)
└──────────────┘
```
---
## 五、关键数据流路径
以"600362 市场分析"为例,追踪一次数据调用的完整路径:
```
1. Market Analyst LLM 决定调用 get_stock_data("600362", ...)
2. LangChain @tool 装饰器触发
→ agents/utils/core_stock_tools.py:28
3. 内部调用 route_to_vendor("get_stock_data", "600362", ...)
→ dataflows/interface.py:140
4. route_to_vendor 读取 config["data_vendors"]["core_stock_data"]
→ {"primary": "akshare"}
5. 按 priority 顺序尝试:
a) akshare → akshare_data.get_stock_data("600362", ...)
- 检测 "600362" 是6位数字 → 路由到 A 股接口
- 调用 ak.stock_zh_a_hist(symbol="600362", period="daily", ...)
- 格式化 DataFrame → Markdown 表格字符串
- 成功!返回数据 ✓
6. route_to_vendor 获得 Markdown 字符串 → 返回给 tool
7. tool 将数据嵌入 LLM context
8. Market Analyst LLM 收到数据,生成技术分析报告
9. 报告写入 state.market_report
```
如果第5步 akshare 失败,route_to_vendor 会自动尝试 yfinance,再失败尝试 alpha_vantage,**完全透明,LLM 无感知**。
---
## 六、耗时分布估算
基于 DeepSeek 推理速度的单次分析耗时(仅供参考):
| 阶段 | LLM 调用次数 | 数据API调用 | 耗时估算 |
|------|-------------|------------|----------|
| 并行分析师(4个) | 8-16次 (LLM + tool) | 8-15次 | 90-180秒 |
| 研究经理汇总 | 1次 | 0 | 10-20秒 |
| 辩论(1轮) | 3次 (Bull/Bear/Judge) | 0 | 30-60秒 |
| 交易员决策 | 1次 | 0-2次 | 15-30秒 |
| 三级风控 | 4次 (3风控+1裁判) | 0 | 40-80秒 |
| **合计** | **17-25次** | **8-17次** | **3-6分钟** |
> **注意**: 如果 `max_debate_rounds` 设为 2 或更多,辩论耗时翻倍。数据源的 Rate Limit 状况也会影响实际耗时。
---
## 七、与原始实现的关键差异
本项目基于开源 TradingAgents 框架,做了以下适应性修改(详见[架构文档](./architecture-and-modifications.md)):
| 差异点 | 原始实现 | 当前实现 |
|--------|---------|---------|
| 数据源 | yfinance 为主 | **akshare 为主**,yfinance 降级为 fallback |
| A股行情 | 不稳定,频繁 Rate Limit | ✅ 稳定,不限流 |
| A股财务数据 | ❌ | ✅ 三大报表完整 |
| 新闻源 | 英文(yfinance news) | ✅ 中文(东方财富 + CCTV) |
| 港股支持 | 部分 | ✅ 完整 |
| Web UI | ❌ 无 | ✅ FastAPI + 前端面板 |
| 部署 | 脚本手动运行 | ✅ systemd 服务自动管理 |
---
## 八、配置调优建议
通过修改 `default_config.py` 或运行时 `set_config()` 可调整的参数:
| 参数 | 默认值 | 建议范围 | 说明 |
|------|--------|---------|------|
| `max_debate_rounds` | 1 | 1-3 | 辩论轮次,越多越深度但越慢 |
| `data_vendors.*.primary` | `"akshare"` | `"akshare"` / `"yfinance"` | 主数据源 |
| `llm_provider` | `"deepseek"` | — | LLM 后端 |
| `quick_thinking` | `False` | `True` / `False` | True 使用 fast model,速度更快精度略低 |
---
## 九、监控与调试
### 查看服务日志
```bash
journalctl -u tradingagents.service -f
```
### 查看分析任务状态
```bash
curl http://129.211.173.87:8500/api/task/{task_id}
```
### 重启服务
```bash
sudo systemctl restart tradingagents.service
```