第一章:A股日线数据获取——用Tushare Pro搞定基础行情
做量化投资,第一步就是拿数据。这话听起来简单,但真干起来,坑不少。
我个人习惯用Tushare Pro。这个库在国内量化圈用得挺广,数据全,更新快。今天咱们就聊聊怎么用它获取A股日线行情,顺便说说数据字段、存储方式,还有增量更新那点事儿。
1.1 准备工作:注册与Token配置
先别急着写代码。你得去Tushare Pro官网注册个账号,然后申请Token。这个Token就是你的通行证,每次调用接口都得带上。
注意: Token别泄露到公开仓库里。我见过有人把Token硬编码到代码里,然后传到GitHub上...结果可想而知。
拿到Token后,配置方式有两种:
- 环境变量法: 设置
TS_TOKEN环境变量,一劳永逸 - 代码直接设: 每次调用前用
pro.set_token('你的token')
我个人推荐第一种。为啥?你想想看,代码里少写一个字符串,就少一分泄露风险。
# 安装
pip install tushare
# 初始化
import tushare as ts
pro = ts.pro_api('你的token') # 或者用环境变量,这行可以省略
1.2 获取日线行情:核心接口
Tushare Pro里获取日线数据的接口叫 daily。参数不多,但有几个关键点得说清楚。
# 获取单只股票日线
df = pro.daily(ts_code='000001.SZ', start_date='20240101', end_date='20240301')
print(df.head())
返回的数据长这样:
| 字段名 | 含义 | 类型 |
|---|---|---|
| ts_code | 股票代码 | str |
| trade_date | 交易日期 | str (YYYYMMDD) |
| open | 开盘价 | float |
| high | 最高价 | float |
| low | 最低价 | float |
| close | 收盘价 | float |
| pre_close | 昨收价 | float |
| change | 涨跌额 | float |
| pct_chg | 涨跌幅(%) | float |
| vol | 成交量(手) | int |
| amount | 成交额(千元) | float |
小技巧: 字段里的
amount 单位是千元,不是元。我刚开始做回测时没注意这个,算出来的收益率差了1000倍...嗯,从那以后我每次看文档都先瞄一眼单位。
1.3 数据存储:选CSV还是数据库?
数据拿到手了,存哪儿?这得看你的使用场景。
- CSV文件: 简单粗暴,适合小规模数据。几百只股票,几年数据,CSV完全够用。
- SQLite: 轻量级数据库,适合个人项目。不用装服务器,一个文件搞定。
- MySQL/PostgreSQL: 团队协作或大规模数据时用。我团队里就用PostgreSQL,支持并行查询,爽得很。
我个人习惯先用CSV做原型验证,等策略稳定了再迁移到数据库。别一上来就搞复杂架构,你想想看,策略都没跑通,数据库再牛也没用。
# 存为CSV
df.to_csv('000001.SZ_daily.csv', index=False)
# 存到SQLite
import sqlite3
conn = sqlite3.connect('ashare.db')
df.to_sql('daily_000001', conn, if_exists='replace', index=False)
conn.close()
1.4 增量更新策略:别每次都全量拉
这是很多新手容易犯的错。每次跑脚本都把全部历史数据拉一遍,浪费流量不说,还容易被Tushare限流。
正确的做法是:只拉取缺失的数据。
具体思路是这样的:
- 先检查本地已有数据的最大日期
- 从最大日期的下一天开始拉取
- 拉完后追加到本地存储
import pandas as pd
from datetime import datetime, timedelta
def update_daily_data(ts_code, db_path='ashare.db'):
# 读取本地数据
try:
local_df = pd.read_sql(f"SELECT * FROM daily_{ts_code}",
f'sqlite:///{db_path}')
last_date = local_df['trade_date'].max()
start_date = (datetime.strptime(last_date, '%Y%m%d') +
timedelta(days=1)).strftime('%Y%m%d')
except:
# 本地没数据,从头拉
start_date = '20000101'
# 拉取新数据
new_df = pro.daily(ts_code=ts_code, start_date=start_date)
if not new_df.empty:
# 追加到数据库
new_df.to_sql(f'daily_{ts_code}', f'sqlite:///{db_path}',
if_exists='append', index=False)
print(f"更新了 {len(new_df)} 条记录")
else:
print("没有新数据")
核心要点: 增量更新的本质是「记录上次拉取的位置」。你可以用数据库记录,也可以用文件记录。我习惯在数据库里建一张
update_log 表,专门记录每只股票的最后更新时间。
1.5 避坑指南:我踩过的几个雷
做数据获取这事儿,看着简单,但细节决定成败。分享几个我亲身经历过的坑:
- 复权问题: Tushare的
daily接口返回的是不复权数据。做回测时记得用adj_factor接口做复权处理。我曾经直接用不复权数据跑回测,结果分红除权那天策略收益率突然暴跌...查了半天才发现是数据问题。 - 停牌数据: 停牌期间,日线数据是缺失的。做因子计算时要注意填充或剔除。我一般用前向填充,简单有效。
- 接口限流: Tushare Pro有调用频率限制。批量拉取时记得加
time.sleep()。我一般每调用一次等0.5秒,稳得很。
1.6 知识体系总览
下面这张图把本章的核心逻辑串起来了。从数据源到存储,再到增量更新,一条线走通。
这张图里,从数据源到接口调用,再到字段解析和存储,最后用增量更新策略收尾。每一步都有坑,但每一步也都有解法。
我的建议: 刚开始做数据获取时,别追求完美。先跑通一条链路,再慢慢优化。数据获取这事儿,80%的时间花在20%的细节上。先把基础打牢,后面做策略分析时才不会手忙脚乱。
好了,第一章就聊到这儿。数据获取是量化投资的基石,这块儿稳了,后面的路就好走了。