多股票行情放进同一张表后,最容易被忽略的不是收益率公式,而是“上一行到底属于谁”。如果数据按交易日整体排序后直接执行 `df["close"].pct_change()`,一只股票当天的第一条记录可能会拿另一只股票昨天的收盘价做比较。代码不会报错,结果却从第一步就串了标的。
我一般先检查 ’symbol + trade_date'是否唯一,再按股票和日期排序,最后在组内计算:
key = ["symbol", "trade_date"]
assert not df.duplicated(key).any()
df = df.sort_values(key)
df["ret"] = (
df.groupby("symbol", sort=False)["close"]
.transform(lambda s: s.pct_change(fill_method=None))
)
这里显式写 ‘fill_method=None’ 很重要。pandas 官方文档说明,‘pct_change’ 计算的是相对变化,不是已经乘以 100 的百分数;当前接口也不再替调用者自动决定怎样填充缺失值。这样做会让缺口保留为 ‘NaN’,便于后面判断原因,而不是静默产生一个看似正常的收益率。
缺失值不能统一前向填充。某天没有价格,可能是停牌、尚未上市、已经退市,也可能只是采集失败。前三类属于交易状态,采集失败则是数据质量问题。若先把宽表全部 ‘ffill()’,缺口日往往会出现人为的 0 收益,复牌或补数时的变化又被集中到另一日,横截面排名、波动率和组合净值都会受影响。
更稳妥的流程是先在每只股票自己的观测序列上算收益,再和交易日历、上市区间及停复牌状态对齐。组合层需要统一日期时,可以单独保存 ‘is_tradable’和 ‘missing_reason’:估值规则、信号规则与成交规则分别读取这些字段,而不是让一个填充值同时承担三种含义。
最后我会抽查每只股票的首条收益是否为空、缺口前后日期是否合理,并统计各类缺失原因。收益率只有一行公式,但数据分组和缺失值语义如果没处理清楚,后面的因子检验再精细也很难可信。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。