This commit is contained in:
Ubuntu
2026-07-03 16:58:28 +08:00
commit 32b43f85bd
6240 changed files with 1987678 additions and 0 deletions
@@ -0,0 +1,471 @@
# 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 链尝试 |