4. 数据准备:获取股票行情数据、财务数据
做Smart Beta组合,说白了就是「数据喂进去,策略吐出来」。但数据这步要是没弄干净,后面再牛的模型也是白搭。我见过太多人,策略回测曲线漂亮得不行,一上实盘就崩——十有八九是数据准备阶段埋了雷。
这一章,咱们就把数据准备的底裤扒干净。
4.1 行情数据怎么拿?
行情数据,就是每天的开高低收、成交量、成交额。我个人习惯用 tushare 或者 akshare,这两个库对国内A股覆盖得比较全。
举个例子,拿日线数据:
import tushare as ts
pro = ts.pro_api('你的token')
# 获取平安银行2023年日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20230101', end_date='20231231')
print(df.head())
嗯,这里要注意:tushare的token要自己去官网申请,免费的额度够个人研究用。如果你在机构做,建议直接买Wind或者聚宽的接口,数据质量更稳定。
我在项目中遇到过一个问题:不同数据源对复权处理不一样。有的默认前复权,有的后复权。做Smart Beta时,我建议统一用后复权数据。为什么?因为后复权保留了真实的收益率信息,计算因子时不会失真。
4.2 财务数据怎么拿?
财务数据是Smart Beta的灵魂。市盈率、市净率、ROE、营收增速……这些因子全得靠财报算。
同样用tushare拿财务数据:
# 获取平安银行2023年一季报财务数据
fin_df = pro.fina_indicator(ts_code='000001.SZ', start_date='20230331', end_date='20230331')
print(fin_df.columns)
财务数据有个坑:财报发布日期和财报覆盖期不一样。比如4月底发布的Q1财报,覆盖的是1-3月的数据。如果你在4月15号用Q1数据做因子,那就犯了「未来函数」的错误——数据还没发布呢!
我曾经因为这个原因,回测跑出来年化20%,实盘直接亏成狗。后来学乖了:一定要用财报的实际发布日期,而不是财报覆盖期。tushare的disclosure_date字段就是干这个用的。
4.3 数据清洗:处理停牌、ST、新股
数据拿到手,别急着算因子。先做三件事:去停牌、去ST、去新股。这三样东西,是Smart Beta组合的隐形杀手。
4.3.1 停牌处理
股票停牌期间,价格不变,成交量是0。如果你不处理,因子计算会出问题。比如波动率因子,停牌期间波动率为0,复牌后一天波动率暴增,这显然不合理。
我的做法是:停牌超过5天的股票,直接剔除。如果只是停牌1-2天,用前一天的收盘价填充。
# 标记停牌:当日成交量为0且涨跌幅为0
df['is_suspended'] = (df['vol'] == 0) & (df['pct_chg'] == 0)
# 连续停牌超过5天,剔除
df['suspend_days'] = df.groupby('ts_code')['is_suspended'].cumsum()
df = df[df['suspend_days'] <= 5]
4.3.2 ST股票处理
ST股票,说白了就是「问题股」。涨跌幅限制变成5%,流动性差,随时可能退市。Smart Beta组合里放ST股票,风险收益特征完全扭曲。
我建议:每个月调仓时,把当前处于ST状态的股票全部剔除。注意,是「当前状态」,不是历史状态。有些股票摘帽后恢复正常,可以重新纳入。
4.3.3 新股处理
新股上市头几天,经常连续涨停,波动率异常大。如果你把新股纳入组合,因子值会被严重扭曲。
我个人习惯:上市不满60个交易日的股票,直接剔除。为什么是60天?因为A股新股通常一个月左右打开涨停板,再给一个月让价格回归正常。
# 计算上市天数
df['list_days'] = (df['trade_date'] - df['list_date']).dt.days
df = df[df['list_days'] >= 60]
4.4 数据对齐:让所有股票站在同一起跑线
数据对齐,就是让不同股票的数据在时间上保持一致。比如你要算2023年1月的因子,那所有股票都得有2023年1月的数据。
对齐的步骤:
- 时间对齐: 把日线数据统一到同一个交易日历上。A股交易日历可以从交易所官网下载,或者用tushare的
trade_cal接口。 - 频率对齐: 财务数据是季频,行情数据是日频。做因子时,通常把财务数据「降频」到月频——每个月用最新的财报数据。
- 缺失值处理: 有些股票在某个月没有财务数据(比如刚上市),直接填充为NaN,后续计算因子时自动跳过。
# 生成月度时间戳
monthly_dates = pd.date_range(start='2023-01-01', end='2023-12-31', freq='M')
# 对齐:每个月底取最新的财务数据
aligned_df = fin_df.groupby('ts_code').resample('M', on='trade_date').last()
4.5 本章知识体系
下面这张图,把数据准备的整个流程串起来了。你可以把它当成一个检查清单,每次做数据时对照着走一遍。
数据准备这步,看着琐碎,但真不能省。我见过有人直接拿原始数据跑因子,结果回测收益高得离谱——仔细一查,原来是把未来数据混进去了。嗯,这种错误犯一次就够了。
数据准备好了,下一章咱们就开始算因子。记住一句话:垃圾进,垃圾出。数据干净了,策略才能站得住脚。