第三章 数据获取:股票数据源选择、Tushare/AkShare接口使用、数据清洗与预处理、数据存储方案
做量化回测,数据就是你的原材料。原材料不行,再好的策略也白搭。我见过太多人花几个月写策略,最后发现数据有坑,回测结果全是假的。今天咱们就把数据获取这件事聊透。
3.1 股票数据源怎么选?
国内主流的数据源,说白了就这几家:Tushare、AkShare、Wind、聚宽、米筐。我个人习惯把数据源分成两类:免费的和收费的。
| 数据源 | 费用 | 特点 | 适合场景 |
|---|---|---|---|
| Tushare Pro | 免费+积分制 | 数据全面,文档规范 | 个人研究、小规模回测 |
| AkShare | 完全免费 | 接口丰富,更新快 | 快速原型、学习练手 |
| Wind | 收费(年费几万) | 数据质量最高 | 机构、实盘级应用 |
| 聚宽/米筐 | 部分免费 | 自带回测平台 | 不想自己搭系统 |
我建议初学者先从AkShare入手。为什么呢?零成本,接口多,踩坑了也不心疼。等你的策略跑通了,再考虑升级到Tushare或者Wind。
3.2 Tushare接口使用实战
Tushare Pro需要注册获取token。注册地址我就不贴了,百度一下就有。拿到token后,先初始化:
import tushare as ts
# 初始化
pro = ts.pro_api('你的token')
# 获取日线数据
df = pro.daily(
ts_code='000001.SZ',
start_date='20230101',
end_date='20231231'
)
print(df.head())
嗯,这里要注意。Tushare的股票代码格式是「6位代码+交易所后缀」。上交所是.SH,深交所是.SZ。我见过有人把平安银行的代码写成000001,结果啥也查不到。
除了日线,Tushare还提供分钟线、财务数据、资金流向等。我个人最常用的是这几个:
daily()- 日线行情fundamental()- 财务指标moneyflow()- 个股资金流向trade_cal()- 交易日历
3.3 AkShare接口使用实战
AkShare的用法更直接。它不需要token,直接pip安装就能用:
import akshare as ak
# 获取A股日线数据
df = ak.stock_zh_a_hist(
symbol="000001",
period="daily",
start_date="20230101",
end_date="20231231",
adjust="qfq" # 前复权
)
print(df.head())
AkShare的接口命名有点随性。比如获取龙虎榜数据,接口名叫stock_lhb_detail_em()。我第一次用的时候找了半天,后来发现它的命名规则是「功能+数据源缩写」。em代表东方财富,sina代表新浪,thsm代表同花顺。
为什么会这样?因为AkShare本质上是把各个网站的数据接口封装了一下。好处是数据源多,坏处是接口不稳定——万一对方网站改版,你的代码可能就挂了。
核心建议: 生产环境用Tushare,原型开发用AkShare。两条腿走路,哪个挂了都不慌。
3.4 数据清洗与预处理
数据拿到手,别急着用。先做清洗。我总结了一套标准流程:
- 去重 - 检查是否有重复行
- 补空 - 处理缺失值
- 对齐 - 统一时间索引
- 复权 - 处理分红送股
- 异常值 - 剔除涨跌停等极端数据
举个例子,缺失值处理:
# 前向填充(用上一个交易日的数据补)
df.fillna(method='ffill', inplace=True)
# 或者直接删除缺失行
df.dropna(inplace=True)
我曾经遇到过一个坑:某只股票停牌了三个月,复牌后直接跌停。如果用前向填充,那三个月的价格都是停牌前的价格,回测结果会严重失真。后来我加了一个判断:如果连续缺失超过5天,就标记为停牌,单独处理。
3.5 数据存储方案
数据清洗完了,得存起来。我推荐三种方案:
| 存储方式 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| CSV文件 | 简单、可读性强 | 查询慢、占用空间大 | 小规模、个人研究 |
| HDF5 | 压缩率高、读写快 | 不支持并发 | 中等规模、单机使用 |
| SQLite/MySQL | 支持查询、并发好 | 配置稍复杂 | 团队协作、生产环境 |
我个人习惯用HDF5。为什么呢?因为量化数据通常是时间序列,HDF5对这种结构化数据支持得很好。而且pandas直接支持to_hdf()和read_hdf(),代码写起来很顺手。
# 存储到HDF5
df.to_hdf('stock_data.h5', key='000001', mode='a')
# 读取
df = pd.read_hdf('stock_data.h5', key='000001')
如果你要存MySQL,记得建索引。我刚开始没建索引,查一次数据要好几秒,加了索引后毫秒级返回。索引建在日期和股票代码上就行。
总结一下: 数据获取这件事,80%的精力花在清洗和存储上。接口调用反而是最简单的。你想想看,数据质量直接决定回测的可信度。数据有坑,策略再牛也是白搭。