3、数据获取与预处理:获取A股行业指数数据、数据清洗与对齐、收益率计算、行业指数标准化处理
做行业轮动,第一步就是搞数据。
很多人一上来就写策略,结果数据源都没搞清楚。我见过太多人,回测跑得飞起,实盘一跑就崩——原因往往是数据没对齐,或者用了未来数据。嗯,这章咱们就把地基打牢。
3.1 数据源选择:Tushare vs AKShare
我个人习惯用Tushare做主力数据源。为什么?因为它的行业分类比较全,历史数据也干净。但Tushare有个门槛——你得注册拿token。如果你嫌麻烦,AKShare是个不错的替代方案,免费且无需注册。
不过要注意,AKShare的数据接口偶尔会变。我曾经在项目中遇到过,前一天还能跑的代码,第二天接口就改了名。所以我的建议是:生产环境用Tushare,研究环境用AKShare。
核心观点:数据源没有绝对的好坏,关键是稳定性和可复现性。选一个你熟悉的,长期用下去。
3.2 获取行业指数数据
我们以申万一级行业为例。Tushare里对应的接口是index_daily,参数填上行业指数代码就行。
代码示例:
import tushare as ts
# 初始化
pro = ts.pro_api('你的token')
# 获取申万银行行业指数
df = pro.index_daily(ts_code='801780.SI',
start_date='20200101',
end_date='20231231')
print(df.head())
如果你用AKShare,代码长这样:
import akshare as ak
# 获取申万一级行业指数
df = ak.sw_index_daily(symbol="801780")
print(df.head())
这里有个坑:不同数据源的行业代码可能不一样。Tushare用.SI后缀,AKShare直接用数字代码。你想想看,如果混着用,代码写错了都不知道。
避坑指南:我曾经在项目中同时用了两个数据源,结果行业分类对不上。后来统一用Tushare的申万分类,才把问题解决。建议你从一开始就固定一个数据源。
3.3 数据清洗与对齐
拿到数据后,第一件事不是算收益率,而是清洗。
常见的脏数据包括:
- 缺失值:某天停牌,数据是NaN
- 异常值:价格突然跳空,可能是数据录入错误
- 日期不连续:节假日、周末数据缺失
我的处理流程是这样的:
- 先检查日期是否连续。用
pd.date_range生成完整日历,然后左连接。 - 缺失值用前向填充(
ffill)。为什么?因为停牌期间,价格应该沿用上一个交易日的数据。 - 异常值用3倍标准差法剔除。如果某天涨跌幅超过均值±3σ,我一般会标记出来人工复核。
代码示例:
import pandas as pd
# 假设df是原始数据,包含date和close列
df['date'] = pd.to_datetime(df['date'])
df = df.set_index('date')
# 生成完整日历
full_calendar = pd.date_range(start='2020-01-01',
end='2023-12-31',
freq='B') # 仅交易日
# 对齐
df = df.reindex(full_calendar)
df['close'] = df['close'].ffill() # 前向填充
# 异常值处理
mean = df['close'].mean()
std = df['close'].std()
df = df[(df['close'] > mean - 3*std) &
(df['close'] < mean + 3*std)]
小技巧:对齐多个行业指数时,建议用pd.concat按列合并。这样能保证所有指数在同一时间轴上,方便后续计算。
3.4 收益率计算
收益率是行业轮动的核心输入。常用的有两种:
| 类型 | 公式 | 适用场景 |
|---|---|---|
| 简单收益率 | (P_t - P_{t-1}) / P_{t-1} | 短期信号,日频交易 |
| 对数收益率 | ln(P_t / P_{t-1}) | 长期分析,多期累加 |
我个人习惯用对数收益率。为什么?因为对数收益率具有可加性,计算多期累计收益时直接相加就行,不会出现复利计算的偏差。
代码示例:
# 计算对数收益率
df['log_return'] = np.log(df['close'] / df['close'].shift(1))
# 计算简单收益率
df['simple_return'] = df['close'].pct_change()
这里要注意:收益率计算时,shift(1)会引入NaN。第一行数据没有前值,所以收益率是空值。记得用dropna()去掉。
3.5 行业指数标准化处理
不同行业的价格水平差异很大。银行指数可能只有3000点,而食品饮料指数可能已经20000点了。直接比较绝对价格没有意义,必须标准化。
常用的标准化方法:
- Z-score标准化:(x - μ) / σ,适用于正态分布的数据
- Min-Max标准化:(x - min) / (max - min),将数据压缩到[0,1]区间
- 百分位排名:将数据转换为0-100的排名值
在行业轮动中,我推荐用滚动Z-score。为什么?因为行业指数的均值和标准差是随时间变化的。用固定窗口(比如60天)计算滚动均值和标准差,能更好地捕捉近期趋势。
代码示例:
# 滚动Z-score标准化
window = 60
df['zscore'] = (df['log_return'] -
df['log_return'].rolling(window).mean()) / \
df['log_return'].rolling(window).std()
关键点:标准化窗口的选择很重要。窗口太短(比如20天),信号会过于敏感;窗口太长(比如250天),信号反应迟钝。我一般用60天,也就是一个季度的交易日数。
3.6 本章知识体系
下面这张图,帮你理清数据获取与预处理的完整流程:
说白了,数据预处理就是四个步骤:获取 → 清洗 → 计算 → 标准化。每一步都有坑,但只要你按流程走,基本不会出大问题。
个人经验:我刚开始做行业轮动时,总想跳过数据清洗这一步。结果回测收益高得离谱,实盘却亏得一塌糊涂。后来才发现,原来是数据里混入了未来信息。从那以后,我每次跑策略前都会先做一遍完整的数据预处理流程。
嗯,数据部分就讲到这里。记住:数据质量决定了策略的上限。再好的模型,喂进去垃圾数据,出来的也是垃圾结果。
公众号:蓝海数据掘金营,微信deep3321