第四节:数据获取——用Tushare和Akshare搞定行情数据
做量化交易,数据就是你的弹药。没有数据,再牛的策略也是纸上谈兵。
我个人习惯把数据获取分成两类:一类是离线批量拉取,存到本地数据库;另一类是实时在线调用,做盘中决策。今天咱们重点讲前者——怎么用Python把股票、期货、ETF的日线行情拿到手。
市面上免费的数据源不少,但我用得最多的就两个:Tushare 和 Akshare。前者老牌、稳定,后者新锐、覆盖面广。我建议你两个都装上,互补使用。
4.1 环境准备与安装
先装包。没什么技术含量,但版本号要注意。
# 安装Tushare(建议用最新版)
pip install tushare==1.4.10
# 安装Akshare
pip install akshare==1.14.0
# 辅助库
pip install pandas numpy requests
嗯,这里要注意:Tushare从1.4.0版本开始,需要注册获取token。免费版有频率限制——每分钟最多200次请求,每天最多5000次。我个人觉得够用了,除非你跑全市场5000只股票。
4.2 用Tushare获取股票日线数据
先初始化。把token配置好,后面就简单了。
import tushare as ts
# 设置token(去官网注册获取)
ts.set_token('你的token')
# 初始化pro接口
pro = ts.pro_api()
# 获取平安银行2024年1月日线数据
df = pro.daily(
ts_code='000001.SZ',
start_date='20240101',
end_date='20240131'
)
print(df.head())
返回的字段包括:trade_date、open、high、low、close、pre_close、change、pct_chg、vol、amount。
这里有个细节:pct_chg是涨跌幅,单位是百分比。比如涨了5%,返回5.0。我刚开始做回测时,直接拿这个值当收益率用,结果发现和复利计算对不上。后来才意识到,它算的是单日简单收益率,不是对数收益率。
4.3 用Tushare获取期货主力连续合约
期货比股票麻烦。因为合约会到期,你需要主力连续合约。Tushare提供了fut_mapping接口,直接帮你映射好。
# 获取螺纹钢主力连续合约日线
df_fut = pro.fut_mapping(
ts_code='RB.SHF', # 螺纹钢,上海期货交易所
start_date='20240101',
end_date='20240131'
)
print(df_fut.head())
返回的字段多了oi(持仓量)和settle(结算价)。做跨品种套利时,我一般用结算价而不是收盘价,因为期货的收盘价容易被尾盘大单影响。
为什么会这样?因为期货市场最后3分钟是集合竞价,容易产生异常价格。我吃过这个亏——有一次做螺纹钢和热卷的套利,收盘价信号显示价差突破,结果第二天开盘就回归了。后来改用结算价,信号稳定多了。
4.4 用Akshare获取ETF行情数据
Akshare在ETF数据上做得比Tushare好。它直接抓取东方财富的数据,覆盖面广,更新及时。
import akshare as ak
# 获取沪深300ETF(510300)日线数据
df_etf = ak.fund_etf_hist_em(
symbol="510300",
period="daily",
start_date="20240101",
end_date="20240131",
adjust="qfq" # 前复权
)
print(df_etf.head())
返回字段包括:日期、开盘、最高、最低、收盘、成交量、成交额、振幅、涨跌幅、涨跌额、换手率。
注意:Akshare返回的列名是中文。我建议你拿到数据后,统一转成英文列名,方便后续处理。
4.5 多品种批量获取与存储
实战中,你不会只拿一只股票。我通常的做法是:先维护一个品种列表,然后循环拉取,最后存到本地CSV或数据库。
import pandas as pd
import time
# 品种列表
symbols = [
'000001.SZ', # 平安银行
'600519.SH', # 贵州茅台
'510300.SH', # 沪深300ETF
'RB.SHF', # 螺纹钢期货
'CU.SHF', # 铜期货
]
def fetch_all_data(symbols, start_date, end_date):
data_dict = {}
for sym in symbols:
try:
if sym.endswith('.SH') or sym.endswith('.SZ'):
# 股票
df = pro.daily(ts_code=sym, start_date=start_date, end_date=end_date)
elif '.SHF' in sym:
# 期货
df = pro.fut_mapping(ts_code=sym, start_date=start_date, end_date=end_date)
else:
# ETF用Akshare
df = ak.fund_etf_hist_em(symbol=sym[:6], period="daily",
start_date=start_date, end_date=end_date)
data_dict[sym] = df
print(f"成功获取: {sym}")
time.sleep(0.5) # 控制频率,别被封
except Exception as e:
print(f"获取 {sym} 失败: {e}")
return data_dict
# 执行
all_data = fetch_all_data(symbols, '20240101', '20240131')
这里我加了time.sleep(0.5),就是为了避免触发Tushare的频率限制。你想想看,如果一次拉100个品种,不加延时的话,很可能第50个就被封了。
4.6 数据质量检查
数据拿到手,别急着用。先做三件事:
- 检查缺失值——看看有没有NaN的行
- 检查日期连续性——交易日之间不能有跳空(除节假日外)
- 检查价格合理性——有没有0值或异常大值
def check_data_quality(df, symbol):
issues = []
# 1. 缺失值
if df.isnull().any().any():
issues.append(f"存在缺失值: {df.isnull().sum().to_dict()}")
# 2. 日期连续性
dates = pd.to_datetime(df['trade_date'])
date_diff = dates.diff().dropna()
if (date_diff > pd.Timedelta(days=5)).any():
issues.append("存在超过5天的日期间隔")
# 3. 价格合理性
if (df['close'] <= 0).any():
issues.append("存在非正收盘价")
if issues:
print(f"{symbol} 数据质量问题: {issues}")
else:
print(f"{symbol} 数据质量通过")
return len(issues) == 0
这个检查函数,我每次拉完数据都会跑一遍。别嫌麻烦,数据质量决定了策略的生死。
4.7 知识体系总览
下面这张图,概括了本章的核心逻辑。你看一眼,就知道数据获取的全貌了。
4.8 实战中的几个建议
| 场景 | 推荐数据源 | 注意事项 |
|---|---|---|
| 股票日线 | Tushare Pro | 注意复权方式,默认不复权 |
| 期货主力连续 | Tushare Pro | 用结算价而非收盘价 |
| ETF行情 | Akshare | 列名是中文,需转换 |
| 可转债 | Akshare | 注意转股溢价率字段 |
| 指数成分股 | Tushare Pro | 需要较高积分权限 |
最后说一句:数据获取是基本功,但很多人栽在这上面。别急着跑策略,先把数据管道搭稳了。我见过太多人,策略逻辑写得漂亮,结果数据源一换,回测结果天差地别。
嗯,今天就到这儿。代码都给你了,自己去跑一遍,感受一下。
- Tushare适合股票和期货,Akshare适合ETF和可转债
- 批量拉取必须加延时,避免被封
- 数据质量检查是必须步骤,不能跳过
- 字段对齐能让你在切换数据源时少改代码