← 返回博客列表

【魔码量化工程实战进阶 #16】横截面排名与选股雏形:每天从5000只里挑50只

2026年08月31日 18:05 · 魔码数服 · 魔码量化工程实战进阶

【魔码量化工程实战进阶 #16】横截面排名与选股雏形:每天从 5000 只里挑 50 只

入门系列都是"单只"视角。本篇进入量化核心范式——横截面(cross-section):同一天,把所有股票摆在一起比,挑出最强的 50 只。这是多因子选股、指数增强、市场中性策略的共同起点。我们用 5 只小面板的真实计算演示"z-score 标准化 + 分位排名"。

本文你将得到什么

  1. 横截面 vs 时序:为什么选股必须用"同一天跨股票比"
  2. z-score 标准化分位排名(pct rank)两种写法与取舍
  3. 中性化雏形:市值/行业中性,避免因子变成"小盘暴露"
  4. 本机复核(合成面板 seed=42):5 只末日 20 日动量 + 分位排名(S0=1.0 … S4=0.6)

一、痛点:单只信号无法"选股"

"茅台 20 日涨 5%"——强吗?不知道。如果当天全市场平均涨 8%,茅台其实跑输。选股要在"同一天"把所有股票摆一起排名,取头部。这就是横截面思维。


二、工程方案

2.1 横截面 z-score

对每一天(每一行),把各股票的因子值标准化:

import pandas as pd
mom20 = panel / panel.shift(20) - 1          # 20日动量(面板,行=日 列=股)
z = (mom20 - mom20.mean(axis=1, skipna=True)) / mom20.std(axis=1, skipna=True)

z 表示"当天这只股票的相对强度,单位是个标准差"。

2.2 分位排名(更稳健)

极值会扭曲 z-score。实盘更常用分位排名(0~1):

rank = mom20.rank(axis=1, pct=True)          # 每行(每天)跨股票排名,0=最弱 1=最强

选股:rank > 0.9 取前 10%。

2.3 中性化雏形

裸动量容易变成"小盘股暴露"(小盘波动大、动量极端)。中性化:先对市值回归取残差,再排名:

# resid = mom20 - β·log_mktcap ;用残差排名,剔除市值影响
neutral = mom20 - 0.3 * log_mktcap          # 简化示意,β 需回归估计

三、本机复核(合成面板 seed=42)

5 只股票的小面板,末日(最后一行)的 20 日动量与分位排名:

末日 20日动量%: S0=6.9%  S1=-0.9%  S2=3.4%  S3=-4.0%  S4=-0.7%
末日分位排名:   S0=1.0   S1=0.4    S2=0.8    S3=0.2    S4=0.6

解读:S0 动量最强(6.9%)排第 1(分位 1.0),S3 最弱(−4.0%)排末(分位 0.2)。rank 只看"相对位置"不看"绝对幅度"——即使全市场都在跌,最强那只仍排 1.0,这正是横截面选股"跌市也能做空/做相对"的根基。


四、原理深挖:四个真实坑

  1. 缺失值污染:某只停牌,当天因子为 NaN。z-score 用 skipna=True,排名用 pct=True(自动忽略 NaN),否则一只 NaN 传染整行。
  2. 极端值扭曲:单只暴涨 50% 会让 z-score 分母(std)失真。用分位排名去极值(winsorize)替代裸 z-score。
  3. 前视偏差:排名必须用 t−1 收盘数据,不能用 t 当天(含当天涨跌)。因子与排名都严格"昨收算、今调仓"。
  4. 幸存者偏差:回测面板若只含"现在还上市"的股票,漏掉了已退市的(它们当年也是候选),选股胜率被高估。用"当时全部上市股票"的截面。

五、小结

横截面排名是"从 5000 只里挑 50 只"的工程内核:z-score 看相对强度、分位排名更抗极值、中性化剔除风格暴露。它把单只指标升级成"全市场可比信号",是多因子选股的第一块基石。下一篇把这个"信号"正式命名为因子,并引入检验因子有没有用的尺子——IC。

横截面选股依赖全市场对齐的后复权面板(#08);魔码量化 Pro 包(完整历史 + 全市场实时)让"每天扫全市场"从算力门槛变成日常操作。详见文末。


免责声明:本文所有示例数据仅用于接口演示,不构成任何投资建议;市场有风险,投资需谨慎。

系列持续更新中。 想要亲手跑通上面的代码?前往 魔码证书申请页 免费领取你的专属证书,复制即用、按次计费、稳定可用。

想亲自试一下?免费获取证书
客服微信
客服微信二维码