新增功能: - 5个专业数据抓取器(公司信息、板块、财务、资金流、龙虎榜) - 一键执行脚本 run_all_fetchers.sh(完整/测试/自定义三种模式) - 扩展数据库Schema(11张新表) - 数据源迁移至免费API(东方财富、新浪、腾讯) 数据能力: - 公司基础信息:5,295只A股 - 板块数据:~400个板块 + 20万成分股关系 - 财务报表:利润表+资产负债表(最近4季) - 资金流:主力资金、大单、中单、小单(700天) - 龙虎榜:上榜记录+营业部明细(2年) - 总数据量:~395万条记录,3-5GB存储 技术亮点: - 断点续传支持 - UPSERT策略避免重复 - API限流控制 - 实时进度显示 - 错误容错机制 - 完整日志记录 文档更新: - README添加"全量数据抓取"章节 - 60+页技术方案文档 - 完整的使用指南和维护文档 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
18 KiB
18 KiB
数据源分析与推荐方案
📊 一、需要获取的数据类型
基于现有系统分析(backend/app/models/stock_models.py 和 backend/database/schema_v2.sql),系统需要以下数据:
1. 股票基本信息 (stocks 表)
| 字段 | 说明 | 示例 |
|---|---|---|
| symbol | 股票代码 | "600000" |
| name | 股票名称 | "浦发银行" |
| market | 市场类型 | "A_SHARE", "US", "HK", "FOREX" |
| industry | 所属行业 | "银行" |
| list_date | 上市日期 | "1999-11-10" |
2. 日线行情数据 (daily_quotes 表)
| 字段 | 说明 | 数据类型 |
|---|---|---|
| symbol | 股票代码 | VARCHAR(20) |
| trade_date | 交易日期 | DATE |
| open | 开盘价 | DECIMAL(12,4) |
| high | 最高价 | DECIMAL(12,4) |
| low | 最低价 | DECIMAL(12,4) |
| close | 收盘价 | DECIMAL(12,4) |
| volume | 成交量 | BIGINT |
| amount | 成交额 | DECIMAL(20,2) |
| turnover_rate | 换手率 | DECIMAL(8,4) |
数据量需求: 至少1年历史数据(约250个交易日/股票)
3. 技术指标数据 (technical_indicators 表)
| 字段 | 说明 | 计算方法 |
|---|---|---|
| ma5, ma10, ma20, ma60 | 移动均线 | 基于收盘价计算 |
| rsi | 相对强弱指标 | RSI(14) |
| macd, macd_signal, macd_hist | MACD指标 | 12-26-9 |
| bollinger_upper/middle/lower | 布林带 | 20日标准差 |
| atr | 平均真实波幅 | ATR(14) |
| obv | 能量潮 | 累计成交量指标 |
计算策略: 从日线数据计算得出,无需单独获取
4. 基本面数据 (fundamentals 表)
| 字段 | 说明 | 更新频率 |
|---|---|---|
| pe_ratio | 市盈率 | 每日 |
| pb_ratio | 市净率 | 每日 |
| ps_ratio | 市销率 | 每季度 |
| roe | 净资产收益率 | 每季度 |
| roa | 资产收益率 | 每季度 |
| gross_margin | 毛利率 | 每季度 |
| net_margin | 净利率 | 每季度 |
| debt_to_equity | 资产负债率 | 每季度 |
| current_ratio | 流动比率 | 每季度 |
| total_revenue | 总营收 | 每季度 |
| net_profit | 净利润 | 每季度 |
| total_assets | 总资产 | 每季度 |
| total_equity | 总权益 | 每季度 |
5. 实时行情数据 (缓存层)
| 字段 | 说明 | TTL |
|---|---|---|
| price | 当前价格 | 60秒 |
| bid | 买一价 | 60秒 |
| ask | 卖一价 | 60秒 |
| volume | 成交量 | 60秒 |
| timestamp | 时间戳 | - |
🔍 二、数据源测试结果
测试方法
- 运行
backend/test_data_sources.py- 测试新浪、东方财富、雪球、网易 - 运行
backend/test_akshare.py- 测试 AkShare 数据库
测试结果汇总
| 数据源 | 实时行情 | 历史K线 | 基本面 | 免费 | 认证 | 稳定性 | 推荐 |
|---|---|---|---|---|---|---|---|
| 新浪财经 | ✅ | ❌ | ❌ | ✅ | 无需 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 东方财富 | ✅ | ⚠️ | ⚠️ | ✅ | 无需 | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| AkShare | ✅ | ✅ | ✅ | ✅ | 无需 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 雪球 | ⚠️ | ❌ | ❌ | ✅ | 需要 | ⭐⭐ | ⭐ |
| 网易财经 | ❌ | ❌ | ❌ | ✅ | 无需 | ⭐ | ⭐ |
| TuShare | ✅ | ✅ | ✅ | ⚠️ | API Key | ⭐⭐⭐⭐ | ⭐⭐ |
图例: ✅ 可用 | ⚠️ 部分可用 | ❌ 不可用
详细测试数据
1. 新浪财经实时行情 ✅
URL: http://hq.sinajs.cn/list=sh600000,sz000001
响应格式: GBK编码文本
优点:
- ✅ 完全免费,无需API key
- ✅ 响应速度快(<100ms)
- ✅ 数据字段丰富(32个字段)
- ✅ 稳定性高,多年未变
缺点:
- ❌ 仅支持实时行情,无历史数据
- ❌ 返回格式为文本,需要解析
可用字段:
{
"name": "浦发银行",
"price": 11.69, # 最新价
"open": 11.72, # 开盘价
"pre_close": 11.78, # 昨收
"high": 11.80, # 最高
"low": 11.65, # 最低
"bid": 11.69, # 买一
"ask": 11.70, # 卖一
"volume": 67815119, # 成交量(股)
"amount": 794336354, # 成交额(元)
"date": "2025-12-19",
"time": "15:00:03"
}
2. 东方财富实时行情 ✅
URL: http://push2.eastmoney.com/api/qt/stock/get
参数: secid=1.600000&fields=f43,f44,f45,f46,f47,f48,f168
响应格式: JSON
优点:
- ✅ 返回JSON格式,易于解析
- ✅ 包含换手率字段(新浪不提供)
- ✅ 数据准确,来源权威
缺点:
- ⚠️ 价格数据需除以100
可用字段:
{
"symbol": "1.600000",
"name": "浦发银行",
"price": 11.69,
"open": 11.72,
"high": 11.80,
"low": 11.65,
"volume": 678151, # 成交量(手)
"amount": 794336354,
"turnover_rate": 0.20 # 换手率%
}
3. AkShare ⭐⭐⭐⭐⭐ (强烈推荐)
安装:
pip install akshare
测试结果:
- ✅ 实时行情: 5790只股票
- ✅ 历史K线: 6211条数据(1999-11-10至今)
- ✅ 基本面: 个股信息、行业分类
- ✅ 股票列表: 5461只
- ✅ 行业分类: 86个行业
代码示例:
import akshare as ak
# 1. 实时行情
df = ak.stock_zh_a_spot_em()
# 返回: 5790只股票的实时数据
# 字段: 代码、名称、最新价、涨跌幅、成交量、成交额、换手率、市盈率、市净率等23个字段
# 2. 历史K线(日线)
df = ak.stock_zh_a_hist(symbol="600000", period="daily", adjust="qfq")
# 返回: 从1999年至今的所有日线数据
# 字段: 日期、开盘、收盘、最高、最低、成交量、成交额、振幅、涨跌幅、涨跌额、换手率
# 3. 个股基本信息
df = ak.stock_individual_info_em(symbol="600000")
# 返回: 总股本、流通股、总市值、行业、上市时间等
# 4. 股票列表
df = ak.stock_info_a_code_name()
# 返回: 5461只A股代码和名称
# 5. 行业分类
df = ak.stock_board_industry_name_em()
# 返回: 86个申万行业分类
# 6. 行业成分股
df = ak.stock_board_industry_cons_em(symbol="房地产服务")
# 返回: 该行业所有成分股及实时行情
优点:
- ✅ 完全免费,无需API key
- ✅ 数据覆盖全面(实时+历史+基本面)
- ✅ 接口简单,返回pandas DataFrame
- ✅ 数据来源权威(东方财富、新浪等)
- ✅ 社区活跃,更新及时
- ✅ 中文文档完善
缺点:
- ⚠️ 数据来源于公开接口,存在反爬风险
- ⚠️ 某些接口可能不稳定
- ⚠️ 不支持分钟级历史数据
🎯 三、推荐方案
方案A: AkShare单一数据源(推荐)⭐⭐⭐⭐⭐
架构:
┌─────────────────────────────────────────────────────────┐
│ 数据采集层 (AkShare) │
├─────────────────────────────────────────────────────────┤
│ - 实时行情: ak.stock_zh_a_spot_em() │
│ - 历史K线: ak.stock_zh_a_hist() │
│ - 基本面: ak.stock_individual_info_em() │
│ - 股票列表: ak.stock_info_a_code_name() │
│ - 行业分类: ak.stock_board_industry_name_em() │
├─────────────────────────────────────────────────────────┤
│ 数据处理层 (feature_engineer.py) │
├─────────────────────────────────────────────────────────┤
│ - 技术指标计算: MA, RSI, MACD, Bollinger, ATR, OBV │
│ - 数据清洗和标准化 │
├─────────────────────────────────────────────────────────┤
│ 缓存层 (Redis) │
├─────────────────────────────────────────────────────────┤
│ - 实时行情: TTL 60s │
│ - 历史数据: TTL 300s │
│ - 基本面: TTL 3600s │
├─────────────────────────────────────────────────────────┤
│ 持久化层 (PostgreSQL) │
├─────────────────────────────────────────────────────────┤
│ - daily_quotes: 日线数据(1年+) │
│ - technical_indicators: 技术指标 │
│ - fundamentals: 基本面数据 │
│ - stocks: 股票基本信息 │
└─────────────────────────────────────────────────────────┘
优点:
- ✅ 架构简单,维护成本低
- ✅ 单一数据源,数据一致性好
- ✅ 完全免费,无API key管理
- ✅ 接口稳定,社区支持好
缺点:
- ⚠️ 单点依赖,若AkShare不可用则全部失败
- ⚠️ 数据源变更风险
适用场景: ✅ 开发阶段、MVP、中小规模生产环境
方案B: 多数据源冗余(高可用)⭐⭐⭐⭐
架构:
┌─────────────────────────────────────────────────────────┐
│ 数据采集层(多源容错) │
├─────────────────────────────────────────────────────────┤
│ 实时行情 (优先级): │
│ 1. AkShare (主) │
│ 2. 新浪财经 (备用1) │
│ 3. 东方财富 (备用2) │
│ │
│ 历史K线: │
│ 1. AkShare (主) │
│ 2. 本地缓存 (备用) │
│ │
│ 基本面: │
│ 1. AkShare (主) │
│ 2. 爬虫 (备用,需开发) │
└─────────────────────────────────────────────────────────┘
代码示例:
async def get_realtime_quote(symbol: str) -> Dict:
"""多数据源容错获取实时行情"""
# 优先级1: AkShare
try:
df = ak.stock_zh_a_spot_em()
quote = df[df['代码'] == symbol].iloc[0]
return parse_akshare_quote(quote)
except Exception as e:
logger.warning(f"AkShare failed: {e}")
# 优先级2: 新浪财经
try:
return fetch_sina_quote(symbol)
except Exception as e:
logger.warning(f"Sina failed: {e}")
# 优先级3: 东方财富
try:
return fetch_eastmoney_quote(symbol)
except Exception as e:
logger.error(f"All sources failed for {symbol}")
raise DataSourceException("No available data source")
优点:
- ✅ 高可用性,单个数据源故障不影响系统
- ✅ 数据交叉验证
缺点:
- ❌ 复杂度高,维护成本大
- ❌ 数据一致性难保证
适用场景: ✅ 大规模生产环境、金融级应用
方案C: 混合模式(平衡方案)⭐⭐⭐⭐
数据源分工:
- 实时行情: 新浪财经(速度快、稳定)
- 历史K线: AkShare(数据完整、免费)
- 基本面: AkShare + 定期爬虫
- 技术指标: 本地计算(基于历史数据)
优点:
- ✅ 发挥各数据源优势
- ✅ 风险分散
缺点:
- ⚠️ 维护复杂度中等
适用场景: ✅ 中等规模生产环境
📋 四、最终推荐
开发阶段(当前): 方案A - AkShare单一数据源
理由:
- 快速迭代,无需API key管理
- 数据覆盖全面,满足所有需求
- 接口简单,开发效率高
- 完全免费,无成本
生产环境(未来): 方案C - 混合模式
迁移路径:
阶段1 (当前): AkShare单一数据源
↓
阶段2 (3个月后): 添加新浪财经实时行情作为备用
↓
阶段3 (6个月后): 评估付费数据源(TuShare Points、东方财富Choice)
↓
阶段4 (1年后): 自建数据爬虫+商业数据源混合
🛠️ 五、实施计划
第一阶段: 数据获取模块重构(本周)
任务:
-
✅ 安装 akshare:
pip install akshare -
创建新的数据获取模块:
backend/app/modules/data_fetcher_v2.py -
实现以下接口:
fetch_stock_list()- 获取股票列表fetch_realtime_quotes(symbols)- 批量实时行情fetch_historical_kline(symbol, start_date, end_date)- 历史K线fetch_stock_info(symbol)- 基本信息fetch_industry_classification()- 行业分类
-
编写单元测试:
backend/tests/test_data_fetcher_v2.py
第二阶段: 数据库初始化(本周)
任务:
- 创建数据库迁移脚本:
backend/db/migration/V001__init_tables.sql - 使用现有 schema_v2.sql
- 初始化股票列表(5461只A股)
- 批量加载1年历史数据(约250个交易日 × 5461只 = 136万条记录)
数据加载策略:
# 分批加载,避免内存溢出
async def load_historical_data():
stock_list = await fetch_stock_list()
for i in range(0, len(stock_list), 100): # 每批100只
batch = stock_list[i:i+100]
for symbol in batch:
df = ak.stock_zh_a_hist(symbol=symbol, period="daily", adjust="qfq")
# 筛选最近1年数据
df_1year = df[df['日期'] >= '2024-12-21']
# 批量插入数据库
await bulk_insert_daily_quotes(df_1year)
await asyncio.sleep(1) # 避免请求过快
第三阶段: API接口适配(下周)
任务:
- 更新
backend/app/routes/recommendations.py - 移除 TuShare 依赖
- 测试所有API端点
第四阶段: 前端适配(下周)
任务:
- 验证前端数据展示正常
- 测试实时刷新功能
📈 六、性能优化建议
1. 缓存策略
# Redis缓存配置
CACHE_CONFIG = {
"realtime_quote": {
"ttl": 60, # 实时行情缓存60秒
"key_pattern": "quote:{symbol}"
},
"historical_kline": {
"ttl": 3600, # 历史K线缓存1小时
"key_pattern": "kline:{symbol}:{period}"
},
"fundamentals": {
"ttl": 86400, # 基本面缓存24小时
"key_pattern": "fund:{symbol}"
}
}
2. 数据预热
# 在交易日开盘前(9:00)预加载热门股票数据
async def prewarm_cache():
top_stocks = await get_top_100_stocks()
for symbol in top_stocks:
# 预加载到Redis
await fetch_and_cache_quote(symbol)
await fetch_and_cache_fundamentals(symbol)
3. 限流策略
# 避免被封IP
import asyncio
from functools import wraps
def rate_limit(calls_per_second=2):
"""限流装饰器"""
min_interval = 1.0 / calls_per_second
last_called = [0.0]
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
elapsed = time.time() - last_called[0]
if elapsed < min_interval:
await asyncio.sleep(min_interval - elapsed)
last_called[0] = time.time()
return await func(*args, **kwargs)
return wrapper
return decorator
@rate_limit(calls_per_second=2)
async def fetch_with_limit(symbol):
return ak.stock_zh_a_hist(symbol=symbol)
🎓 七、参考资料
AkShare 官方文档
- 官网: https://akshare.akfamily.xyz/
- GitHub: https://github.com/akfamily/akshare
- 接口文档: https://akshare.akfamily.xyz/data/stock/stock.html
数据源对比
| 数据源 | 优势 | 劣势 | 官网 |
|---|---|---|---|
| AkShare | 免费、全面 | 反爬风险 | akshare.akfamily.xyz |
| TuShare | 数据权威 | 需积分 | tushare.pro |
| 新浪财经 | 实时快速 | 仅实时 | finance.sina.com.cn |
| 东方财富 | JSON格式 | 接口变化 | quote.eastmoney.com |
📝 八、总结
当前状态: 已完成数据源调研和验证
下一步行动:
- ✅ 实施方案A(AkShare单一数据源)
- 创建
data_fetcher_v2.py模块 - 初始化数据库(加载1年历史数据)
- 更新API接口
- 测试端到端流程
预期收益:
- ❌ 移除 TuShare 依赖(节省 API token 管理成本)
- ✅ 完全免费的数据获取方案
- ✅ 更简单的架构,降低维护成本
- ✅ 更快的开发迭代速度
风险:
- ⚠️ AkShare 单点依赖(通过本地数据库缓存缓解)
- ⚠️ 反爬风险(通过限流和User-Agent轮换缓解)
生成时间: 2025-12-21 版本: v1.0 作者: Claude Code