Files
nanocode/TradingAgents/docs/architecture-and-modifications.md
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

472 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 技术架构分析及 akshare 集成修改
> **文档时间**: 2026-05-05
> **项目路径**: `/home/ubuntu/forgithub/TradingAgents`
> **运行端口**: `:8500` (Web UI)
---
## 一、项目概述
TradingAgents 是一个基于 LLM 的多智能体(Multi-Agent)金融交易分析框架。它模拟真实投资决策中的"研究员→辩论→交易员"流程,由多个分析师角色协作完成对单只股票的深入分析,并输出最终交易决策(买入/卖出/持有)。
核心思想:**用 LLM 扮演不同角色的分析师,使用真实市场数据工具,通过辩论机制达成投资共识。**
---
## 二、技术栈
| 层次 | 技术选型 |
|------|----------|
| 多智能体编排 | LangGraph(StateGraph) |
| LLM 接口 | LangChain + DeepSeek / OpenAI |
| 工具系统 | LangChain `@tool` 装饰器 |
| 数据源 | akshare / yfinance / Alpha Vantage |
| Web 服务 | FastAPI + 自定义 SPA 页面 |
| 部署 | systemd(`tradingagents.service`) |
---
## 三、架构分层
### 3.1 整体流程
```
用户输入 (ticker + date)
│
▼
┌──────────────────────┐
│ TradingAgentsGraph │ ← LangGraph StateGraph
│ (trading_graph.py) │
└──────┬───────────────┘
│
▼
┌───────────────────────────────────────┐
│ Researcher Team │
│ ┌──────────┐ ┌──────────┐ │
│ │ Market │ │ News │ ... │ ← 并行分析
│ │ Analyst │ │ Analyst │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ └─────┬──────┘ │
│ ▼ │
│ Debate Layer (1~N rounds) │ ← 多轮辩论
│ │ │
│ ▼ │
│ Final Decision │
│ (Buy/Sell/Hold) │
└───────────────────────────────────────┘
│
▼
输出:分析报告 + 交易决策
```
### 3.2 节点说明
| 节点 | 文件 | 角色 | 使用的工具 |
|------|------|------|-----------|
| **Market Analyst** | `analysts/market_analyst.py` | 技术面分析师 | `get_stock_data`, `get_indicators` (RSI, MACD, 布林带等 19 项指标) |
| **News Analyst** | `analysts/news_analyst.py` | 宏观新闻分析师 | `get_news`, `get_global_news` |
| **Social Media Analyst** | `analysts/social_media_analyst.py` | 社交媒体分析师 | `get_news` |
| **Fundamentals Analyst** | `analysts/fundamentals_analyst.py` | 基本面分析师 | `get_fundamentals`, `get_balance_sheet`, `get_cashflow`, `get_income_statement` |
| **Debate Layer** | `trading_graph.py` | 多智能体辩论 | 各分析师报告交叉辩论 |
| **Final Decision** | `trading_graph.py` | 最终交易决策 | 综合所有报告和辩论结果 |
### 3.3 数据流
```
每个 Analyst 节点
│
├─ 构造 system prompt + 数据查询指令
├─ 通过 LangChain tool 调用 route_to_vendor()
├─ route_to_vendor 按优先级选择数据源
│ ├─ akshare (默认)
│ ├─ yfinance (fallback)
│ └─ alpha_vantage (fallback)
└─ 返回格式化数据,LLM 生成分析报告
```
---
## 四、核心模块
### 4.1 Graph 编排层
**文件**: `tradingagents/graph/trading_graph.py`
- 基于 LangGraph `StateGraph` 构建
- `setup_graph()` 按选定的分析师列表动态添加节点
- 支持配置 `max_debate_rounds`(默认 1 轮,可多轮辩论)
- 输出最终状态和交易决策
**文件**: `tradingagents/graph/setup.py`
- 提供 `create_complete_graph()` 工厂函数
- 封装完整流程:研究员 → 辩论 → 终判
### 4.2 工具层
**文件**: `tradingagents/agents/utils/agent_utils.py`
- `create_completion()` — LLM 调用封装,支持 reasoning_effort 参数
- `ToolKit` — 工具集管理,按分析师类型分发工具
**工具定义文件**(位于 `agents/utils/`):
| 文件 | 包含工具 |
|------|---------|
| `core_stock_tools.py` | `get_stock_data` |
| `technical_indicators_tools.py` | `get_indicators` (19 项技术指标) |
| `fundamental_data_tools.py` | `get_fundamentals`, `get_balance_sheet`, `get_cashflow`, `get_income_statement` |
| `news_data_tools.py` | `get_news`, `get_global_news`, `get_insider_transactions` |
所有工具统一通过 `route_to_vendor(method, *args)` 调用,实现数据源透明切换。
### 4.3 数据源层
**入口**: `tradingagents/dataflows/interface.py`
```
route_to_vendor(method, *args)
│
├─ 读取 config["data_vendors"] 确定主数据源
├─ 按 fallback 链依次尝试:
│ ├─ akshare (优先级最高)
│ ├─ yfinance
│ └─ alpha_vantage
└─ 返回第一个成功结果
```
**配置系统**: `tradingagents/dataflows/config.py` + `tradingagents/default_config.py`
- `default_config.py` 定义全局默认配置
- `config.py` 提供运行时 `get_config()` / `set_config()` 接口
- 可通过 `set_config({"data_vendors": {"...": "akshare"}})` 动态切换
### 4.4 Web 服务层
**文件**: `web_server.py`
- FastAPI 应用,端口 `8500`
- `POST /api/analyze` — 提交分析任务(后台异步执行)
- `GET /api/task/{task_id}` — 查询任务状态
- `GET /` — 内置 Web 面板(`web.html`)
---
## 五、akshare 集成修改(详细改动点)
### 5.1 修改动机
原始项目的默认数据源是 `yfinance`,存在以下问题:
1. **国内访问不稳定**: yfinance 对非美股数据支持有限,频繁触发 Rate Limit
2. **A 股数据质量差**: yfinance 的 A 股 OHLCV 数据常常缺失或不准确
3. **新闻源偏西式**: yfinance 的新闻来源主要是英文媒体,对 A 股分析不友好
4. **无国内数据源**: 缺少针对中国市场的本地化数据适配
**解决方案**: 集成 **akshare**(国内开源金融数据接口库)作为主数据源,yfinance 降级为 fallback。
### 5.2 文件变更清单
| 文件 | 类型 | 行数 | 说明 |
|------|------|------|------|
| `tradingagents/dataflows/akshare_data.py` | **新增** | 563 | A股/港股/美股行情、财务数据、技术指标适配 |
| `tradingagents/dataflows/akshare_news.py` | **新增** | 165 | 个股新闻(东方财富)、全球新闻(CCTV)适配 |
| `tradingagents/dataflows/interface.py` | **修改** | +22 | 注册 akshare 到 VENDOR_METHODS,调优 fallback 顺序 |
| `tradingagents/default_config.py` | **修改** | +14/-8 | 默认 vendor 改为 akshare |
| `web_server.py` | **新增** | 124 | FastAPI Web 服务 + 后台任务 |
| `web.html` | **新增** | 480 | 前端分析面板 |
### 5.3 akshare_data.py 详细设计
#### 5.3.1 市场检测
```python
def _detect_market(symbol: str) -> str:
"""自动识别股票所属市场"""
# 6位数字 → A股上海/深圳
# 5位数字 → 港股
# 字母 → 美股
```
通过 ticker 格式自动路由到正确的 akshare 接口:
- `600xxx` / `000xxx` / `300xxx` → A 股(`ak.stock_zh_a_hist`)
- `00700` / `09988` → 港股(`ak.stock_hk_hist`)
- `AAPL` / `MSFT` / `TSLA` → 美股(`ak.stock_us_hist`)
#### 5.3.2 实现的接口
| 接口 | 签名 | akshare 函数 | 说明 |
|------|------|-------------|------|
| `get_stock_data` | `(symbol, start_date, end_date)` | `stock_zh_a_hist` / `stock_hk_hist` / `stock_us_hist` | 三市场统一 OHLCV 数据 |
| `get_indicators` | `(symbol, indicator, curr_date, look_back_days)` | `stock_zh_a_hist` + 手动计算 | 19 项技术指标(RSI/MACD/布林带等) |
| `get_fundamentals` | `(ticker, curr_date)` | `stock_individual_info_em` | 公司概况/市盈率/市净率等 |
| `get_balance_sheet` | `(ticker, freq, curr_date)` | `stock_balance_sheet_by_report_em` | 资产负债表(年报/季报) |
| `get_cashflow` | `(ticker, freq, curr_date)` | `stock_cash_flow_sheet_by_report_em` | 现金流量表 |
| `get_income_statement` | `(ticker, freq, curr_date)` | `stock_profit_sheet_by_report_em` | 利润表 |
| `get_insider_transactions` | `(ticker)` | — | 抛出异常触发 fallback 到 yfinance |
#### 5.3.3 技术指标计算实现
采用**原始公式重算**而非依赖第三方库,确保数据一致性:
```
rsi → 100 - (100 / (1 + RS)) [平均涨跌幅比值法]
macd → EMA(12) - EMA(26), Signal=EMA(9) [指数加权移动平均]
bb → MA ± 2*STD [布林带]
atr → TR 的 Wilder 平滑 [真实波幅]
sma_20 → MA(20)
ema_20 → EMA(20)
```
共支持 19 项常用技术指标,覆盖趋势类、动量类、波动类、成交量类。
#### 5.3.4 输出格式
所有接口返回统一格式:**Pandas DataFrame → Markdown 表格字符串**,与 yfinance 适配器输出格式一致,保证 LLM 可正常解析。
```markdown
| Date | Open | High | Low | Close | Volume |
|------------|--------|--------|--------|--------|-----------|
| 2026-04-25 | 12.50 | 12.80 | 12.30 | 12.65 | 12345678 |
```
#### 5.3.5 TQDM 噪音抑制
akshare 内部使用 tqdm 显示进度条,在 LLM 工具调用场景中会产生大量噪音输出。通过在 akshare import 之前设置环境变量解决:
```python
os.environ.setdefault('TQDM_DISABLE', '1')
import akshare as ak
```
### 5.4 akshare_news.py 详细设计
#### 5.4.1 实现的接口
| 接口 | 说明 | 数据来源 |
|------|------|---------|
| `get_news(ticker, start_date, end_date)` | 个股新闻 | `ak.stock_news_em`(东方财富) |
| `get_global_news(curr_date, look_back_days, limit)` | 宏观/全球新闻 | `ak.news_cctv`(CCTV 新闻联播) |
#### 5.4.2 Ticker 规范化
```
600362 → 600362 (A股,不变)
000001 → 000001 (A股,不变)
AAPL → AAPL (美股,不变)
→ 回退到原 ticker
```
#### 5.4.3 东方财富新闻接口特点
- 返回 15 天内个股相关新闻,含标题、发布时间、来源
- 自动按日期过滤,限制返回最近 50 条
- 格式化输出为 Markdown 表格,便于 LLM 分析
#### 5.4.4 CCTV 新闻接口特点
- 获取新闻联播文字稿摘要
- 适合宏观政策面分析
- 输出为时间线格式的文本列表
### 5.5 interface.py 修改详情
#### 5.5.1 VENDOR_METHODS 注册
为以下 9 个方法注册了 akshare 实现:
```python
VENDOR_METHODS = {
"get_stock_data": {"akshare": ..., "yfinance": ..., "alpha_vantage": ...},
"get_indicators": {"akshare": ..., "yfinance": ...},
"get_fundamentals": {"akshare": ..., "yfinance": ..., "alpha_vantage": ...},
"get_balance_sheet": {"akshare": ..., "yfinance": ..., "alpha_vantage": ...},
"get_cashflow": {"akshare": ..., "yfinance": ..., "alpha_vantage": ...},
"get_income_statement": {"akshare": ..., "yfinance": ..., "alpha_vantage": ...},
"get_news": {"akshare": ..., "yfinance": ...},
"get_global_news": {"akshare": ..., "yfinance": ...},
"get_insider_transactions": {"akshare": ..., "yfinance": ..., "alpha_vantage": ...},
}
```
#### 5.5.2 Fallback 顺序优化
`get_insider_transactions` 的字典键顺序调整为 **akshare → yfinance → alpha_vantage**:
```
原始顺序: alpha_vantage → yfinance → [无 akshare]
修改后: akshare → yfinance → alpha_vantage
```
原因:如果 alpha_vantage 排在 yfinance 之前,当 akshare 不支持时,会先尝试 alpha_vantage —— 它的 `get_api_key()` 在无 API key 时会抛出 `ValueError`(非 `AlphaVantageRateLimitError`),导致异常直接传播、不会继续尝试 yfinance。
调整后 fallback 路径:
1. `akshare` → 抛出 `AlphaVantageRateLimitError`(触发 fallback)
2. `yfinance` → 获取数据(成功)✓
#### 5.5.3 数据源分类优化
将 akshare 明确归入 `all_available_vendors`,确保它在所有 `data_vendors` 分类组中可用:
```python
all_available_vendors = ["akshare", "yfinance", "alpha_vantage"]
```
### 5.6 default_config.py 修改
```python
# 修改前
"data_vendors": {
"core_stock_data": {"primary": "yfinance"},
"technical_indicators": {"primary": "yfinance"},
"fundamental_data": {"primary": "yfinance"},
"news_data": {"primary": "yfinance"},
}
# 修改后
"data_vendors": {
"core_stock_data": {"primary": "akshare"},
"technical_indicators": {"primary": "akshare"},
"fundamental_data": {"primary": "akshare"},
"news_data": {"primary": "akshare"},
}
```
效果:所有分析请求默认走 akshare,彻底避免 yfinance Rate Limit 问题。
### 5.7 web_server.py 设计
#### 5.7.1 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/` | Web 分析面板(`web.html`) |
| GET | `/api/health` | 健康检查 |
| POST | `/api/analyze` | 提交分析任务,返回 task_id |
| GET | `/api/task/{task_id}` | 查询任务状态和结果 |
#### 5.7.2 异步任务机制
```python
class AnalysisRequest(BaseModel):
ticker: str # 股票代码
date: Optional[str] # 分析日期,默认今天
analysts: Optional[list] # 分析师列表,默认 ["market", "news"]
max_debate_rounds: int # 辩论轮次,默认 1
```
分析流程在 `BackgroundTasks` 中异步执行,通过线程安全的 `tasks_store` 字典管理任务状态。
### 5.8 web.html 设计
- 纯原生 HTML/CSS/JS 单页应用
- 响应式设计,支持移动端
- 输入框:股票代码 + 日期 + 分析师选择 + 辩论轮次
- 实时轮询任务状态(每 3 秒)
- 结果展示:Markdown 渲染的交易决策和分析报告(使用 marked.js)
- 适配 DeepSeek 推理过程的 `reasoning_content` 展示
---
## 六、部署架构
```
http://129.211.173.87:8500/
│
┌────────┴────────┐
│ web_server.py │
│ (FastAPI) │
│ port :8500 │
└────────┬────────┘
│
┌────────┴────────┐
│ TradingAgents │
│ Graph │
│ (LangGraph) │
└──────┬─────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ akshare │ │yfinance │ │ alpha │
│ (主要) │ │(备用1) │ │vantage │
└─────────┘ └─────────┘ └─────────┘
```
- **进程管理**: systemd `tradingagents.service`
- **LLM 后端**: DeepSeek(通过 `DEEPSEEK_API_KEY` 环境变量)
- **数据源**: akshare(默认,国内数据丰富)+ yfinance(fallback,美股内部交易等场景)
---
## 七、修改前后对比
| 维度 | 修改前 | 修改后 |
|------|--------|--------|
| **默认数据源** | yfinance | **akshare** |
| **A 股行情** | 不稳定,频繁 Rate Limit | ✅ 稳定,无限制 |
| **A 股财务数据** | ❌ 不可用 | ✅ 完整(资产负债表/利润表/现金流) |
| **技术指标** | 依赖 yfinance 返回 | ✅ 原始公式重算,19 项指标 |
| **个股新闻** | 英文来源,A 股覆盖差 | ✅ 东方财富,中文,时效性强 |
| **宏观新闻** | 英文来源 | ✅ CCTV 新闻联播,政策面 |
| **港股支持** | 部分 | ✅ 完整 |
| **Rate Limit** | 频繁触发 | ✅ 已消除 |
| **内部交易** | yfinance | akshare 自动 fallback 到 yfinance |
| **Web UI** | ❌ 无 | ✅ 新增 FastAPI + Web 面板 |
| **任务异步** | ❌ 无 | ✅ 后台任务 + 状态查询 |
| **TQDM 噪音** | — | ✅ 已抑制 |
---
## 八、项目目录结构
```
TradingAgents/
├── tradingagents/
│ ├── agents/
│ │ ├── analysts/ # 四个分析师角色
│ │ │ ├── fundamentals_analyst.py
│ │ │ ├── market_analyst.py
│ │ │ ├── news_analyst.py
│ │ │ └── social_media_analyst.py
│ │ └── utils/ # 工具定义和 LLM 封装
│ │ ├── agent_utils.py # LLM 调用 + ToolKit
│ │ ├── core_stock_tools.py # get_stock_data
│ │ ├── technical_indicators_tools.py # get_indicators
│ │ ├── fundamental_data_tools.py # 财务分析工具
│ │ └── news_data_tools.py # 新闻工具
│ ├── dataflows/ # 数据源层
│ │ ├── interface.py # 路由分发 + VENDOR_METHODS
│ │ ├── config.py # 运行时配置
│ │ ├── akshare_data.py # 🆕 akshare 行情+财务适配
│ │ ├── akshare_news.py # 🆕 akshare 新闻适配
│ │ ├── yfinance_news.py # yfinance 新闻
│ │ ├── y_finance.py # yfinance 行情
│ │ └── alpha_vantage*.py # Alpha Vantage 适配
│ ├── graph/ # LangGraph 编排
│ │ ├── trading_graph.py # 主图定义
│ │ └── setup.py # 图工厂
│ └── default_config.py # 默认配置(已改为 akshare)
├── docs/ # 文档
│ ├── architecture-and-modifications.md # 🆕 本文档
│ └── ...
├── web_server.py # 🆕 FastAPI Web 服务
├── web.html # 🆕 前端面板
└── pyproject.toml # 依赖管理
```
---
## 九、关键代码路径
| 功能 | 起点 | 链路 |
|------|------|------|
| 用户提交分析 | `web_server.py:94 POST /api/analyze` | → `run_analysis_task` → `TradingAgentsGraph.propagate()` |
| 获取行情数据 | `core_stock_tools.py:get_stock_data` | → `route_to_vendor("get_stock_data", ...)` → `akshare_data.get_stock_data()` |
| 获取技术指标 | `technical_indicators_tools.py:get_indicators` | → `route_to_vendor` → `akshare_data.get_indicators()` |
| 获取财务数据 | `fundamental_data_tools.py:get_fundamentals` | → `route_to_vendor` → `akshare_data.get_fundamentals()` |
| 获取新闻 | `news_data_tools.py:get_news` | → `route_to_vendor` → `akshare_news.get_news()` |
| 数据源切换 | `interface.py:route_to_vendor` | 按 config → fallback 链尝试 |