做因子分层回测时,常见写法是按交易日把股票等分成十组,再观察各组收益是否单调。代码看起来很简单:`pd.qcut(factor, 10)`。但如果因子里有大量重复值,十组未必能顺利生成,最典型的场景是停牌标记、离散评分,或者经过截断后大量样本都落在同一个边界上。
pandas 官方文档说明,`qcut` 是按样本排名或样本分位点切箱;当分箱边界不唯一时,默认会抛出异常。把 `duplicates="drop"` 加上去虽然能继续运行,但它会直接减少箱数。原本设计的十组可能只剩七组,如果后面的代码仍把标签 0 到 9 当成固定组合,收益统计就会出现空组,跨日期比较也会失去统一含义。
我更倾向于先检查当日有效样本数和不同取值数量,再决定分组方式:
def make_group(s, n=10):
valid = s.dropna()
if len(valid) < n or valid.nunique() < n:
return pd.Series(pd.NA, index=s.index, dtype="Int64")
rank = valid.rank(method="average", pct=True)
group = ((rank * n).clip(upper=n) - 1).astype("Int64")
return group.reindex(s.index)
df["group"] = df.groupby("trade_date")["factor"].transform(make_group)
这里使用平均排名,是为了让相同因子值留在同一组,不靠原始行顺序强行拆开并列样本。代价是每组股票数不一定完全相等,但这通常比“同一个因子值随机分到不同组合”更容易解释。若业务确实要求每组数量接近,可以使用 `rank(method="first")` 后再切分,不过必须先按稳定字段排序,否则数据读取顺序变化就可能改变分组结果。
回测输出里最好同时记录计划组数、实际组数、每组样本量和分箱边界。遇到有效股票过少、唯一值不足或某组占比异常时,直接跳过该日或标记质量问题,而不是悄悄补齐标签。分层回测关注的不只是能否算出收益,更重要的是每天的组合定义是否一致、可解释、可复现。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。