机器学习排序与指标错配
第 10 章的截面动量、第 11 章的多因子,其实都已经在做「排序」——按过去收益排、按因子值排,然后买最强的那一档。但那都是单变量排序:一个指标定输赢。真实研究里,你手里往往同时有几十个特征:动量、换手、波动、量价形态、财务指标……它们各说各话,有时互相矛盾。怎么把它们拧成一股绳、合成一个总分?
这一章讲当代选股研究的主流答案——机器学习排序(Learning to Rank, L2R),以及它最著名的陷阱:指标错配(objective mismatch)——排序排得再准,也可能赚不到钱。这一对概念放在一起讲,因为陷阱恰恰是工具的代价。
为什么不用回归,也不用分类
Section titled “为什么不用回归,也不用分类”把「给股票打分」交给机器学习,教科书里有三条路,先看两条死路:
回归:预测明天涨百分之多少。死因是第 2 章讲过的现实——个股日收益的噪声是信号的几十倍,模型辛辛苦苦预测出「+1.23%」,实际值在 ±3% 里晃。当你唯一能用的是「预测值的大小」时,这点精度立刻被噪声淹没。
分类:预测明天涨还是跌。听起来合理,死因有两个。其一,基准率作祟:A 股一段普涨行情里,「预测明天上涨」的机械准确率本来就有 55%+,模型报 58% 你根本分不清是本事还是行情;其二,涨跌对称 ≠ 盈亏对称:预测「涨」的票平均涨 0.5%、预测「跌」的票平均跌 3%,55% 的准确率照样亏钱——分类损失函数对幅度完全无感。
排序:不预测任何一只股票的绝对涨跌,只预测同一批股票之间的相对强弱。它背后是第二编反复出现的一个实证事实(第 10 章五分位、第 11 章分层都见过):个股绝对收益几乎不可预测,但截面的相对次序有微弱而稳定的可预测性。既然可预测的只有「谁比谁强」,那就让模型直接学这个。
排序模型在学什么:成对比较
Section titled “排序模型在学什么:成对比较”排序学习的目标函数不长成「预测值 − 真实值」的平方,而是长成一场场比赛。每个交易日是一个「查询组」:组内 50 只股票两两配对,每一对问一个问题——这两个谁下期更强?答错就罚。最常用的成对损失(pairwise logistic loss):
人话解释: 是股票 的真实下期收益, 是模型给它的分数。对于每一对「真实上 比 强」的股票,如果分数 没有高出 ,损失很大;高出一截,损失趋近 0。模型从头到尾不知道什么叫「涨 2%」,它只知道「A 应该排在 B 前面」。几千场比赛罚下来,分数就学会了次序。
这类目标在信息检索领域叫 ranknet / LambdaRank 家族,工程上常用梯度提升树实现(如 LightGBM 的 ranker 模式)。下一节用一小段把这个工具的思想讲清楚——本教程照例只讲原理:换一棵树、换一个库,成对比较的思想不变。
工具一角:梯度提升树是什么
Section titled “工具一角:梯度提升树是什么”上面点名的 LightGBM,属于一个叫梯度提升树(Gradient Boosted Decision Tree, GBDT)的模型家族。它的思想一句话就能说完:先种一棵很浅的小树,再让后面的每一棵小树专职纠正前面所有树还没改完的错。
人话解释:第一棵树粗糙地把股票分成几档(比如「换手率高且动量强的给 80 分,其余 50 分」),错漏百出;第二棵不看原题,只看第一棵树的残差——哪些股票被排错了次序,专门修这些错;第三棵修前两棵剩下的错……几百棵小树各管一小块,加起来就是一个能表达复杂规律的打分器。每棵树都很笨(通常只有几十片叶子),但「逐次纠错」让整体足够聪明——这是「三个臭皮匠赛过诸葛亮」的精确数学版。
为什么金融表格数据(几十列特征 × 几百万行)的事实标准是这个家族,而不是名气更大的神经网络?三个原因都长在数据的天性上:
- 特征的意义藏在「交互」里。「缩量」单独看没意义,「缩量 + 已经横盘很久」才有意义——树的每一次分叉恰好就是一个「如果…就…」的交互判断,天生合拍;
- 特征的量纲乱七八糟。换手率是百分比、市值以千亿计、动量是小数——树只问「比阈值大还是小」,对单调变换完全无感,不需要归一化;
- 样本少、噪声大。A 股全市场十几年也就百万量级的「股票-日」样本,且信噪比极低——小树的「笨」反而是护栏,让模型没那么容易把噪声背下来(第 6 章的幽灵在这里同样游荡)。
往后再遇到「用 GBDT 类模型跑基准」,脑子里对应的就是这套「小树逐次纠错」的打分器。怎么训练、怎么调参属于工程细节,本教程不展开——它和本章主题的关系只有一条:排序目标决定它学什么,树只是它用来学的东西。
度量排序:NDCG 手算
Section titled “度量排序:NDCG 手算”模型交卷了,怎么打分?直接看预测排序和真实排序像不像。最常用的指标叫 NDCG,拆开看:
DCG(折价累积增益):把真实收益当作「增益」,按模型排的名次加权求和,排得越靠后折价越狠:
人话解释:排第 1 名的增益除以 (不折价),第 2 名除以 ,第 3 名除以 ,第 5 名除以 ……名次每靠后一位,能兑现的增益就打折。为什么这样设计?因为使用者只看得见前几名——你买的是 Top-5,第 4 名排对了还是第 40 名排对了,对你的价值天差地别。
手算一遍:5 只股票,模型打分排序为 A、B、C、D、E,真实下期收益:
| 模型名次 | 股票 | 真实收益 | 折价除数 | 折价后增益 |
|---|---|---|---|---|
| 1 | A | +6.0% | 1 | +6.00 |
| 2 | B | −1.0% | 1.585 | −0.63 |
| 3 | C | +4.5% | 2 | +2.25 |
| 4 | D | −3.0% | 2.322 | −1.29 |
| 5 | E | +2.0% | 2.585 | +0.77 |
完美排序(按真实收益降序:A、C、E、B、D)能拿到的上限:
NDCG ∈ [0, 1],越接近 1 排得越准(负增益如何处理有多种约定变体,自比时口径一致即可)。
度量赚钱:Lift 手算
Section titled “度量赚钱:Lift 手算”NDCG 是检索味儿的指标,交易员更常用 Top-K Lift(提升度)——模型前 K 名的平均收益,相对随机基准放大了多少倍:
手算一遍(沿用上表):模型 Top-2 是 A 和 B,平均收益 ;全体平均 。于是:
随机选 2 只的期望 Lift = 1.0,模型把它抬高了 47%——这就是「排序能力」翻译成钱的汇率。
指标错配:Lift 1.47 的模型,实盘输给随机
Section titled “指标错配:Lift 1.47 的模型,实盘输给随机”到这里一切都很美好。然后是本章的标题陷阱。
上面所有计算都在一个平行世界里:收盘打分,收盘价成交,无摩擦。真实交易的世界是:打分在收盘后,买入最早发生在次日;而排序模型最偏爱的那些「最强」的票,恰恰有四个特征:① 分数高常常正因为它刚刚暴涨过(动量类特征天然奖励「刚涨完的」);② 次日高开,把一部分未来收益提前透支;③ 涨停封板的一字单排队买不进(下一章详述);④ 双边成本要扣。
手算一遍这个世界的账。沿用上表,纸面 Top-2 平均 +2.5%:
| 股票 | 纸面收益 | 现实修正 | 可交易收益 |
|---|---|---|---|
| A | +6.0% | 打分日已涨停买不进,次日 +5% 高开追入:起点抬到 1.05 倍,实得 ,再扣双边成本 0.3% | +0.65% |
| B | −1.0% | 次日 +0.5% 高开:,扣成本 0.3% | −1.79% |
| 组合 | +2.5% | −0.57% |
随机组合(无追高溢价):全体均值 1.7%,扣成本后约 +1.4%。
Lift 1.47 的模型,可交易收益 −0.57%,跑输随机买入近 2 个百分点。 排序没有错——A 确实是未来最强的票;错的是「可交易收益」的定义里藏了一个模型看不见的项:入场溢价随分数递增。分数越高,你为「共识已经形成」付出的价格越贵。写成一行:
当 递增得足够快,:分数越高,买它越亏。模型辛苦学到的排序能力,在交易层被入场溢价反向吃光。这就是指标错配——你优化的指标(NDCG、Lift)和你真正要的东西(可交易利润)不再是同一个函数。
图:12 个月的纸面 vs 可交易
Section titled “图:12 个月的纸面 vs 可交易”本章的演示数据(指标错配演示(合成):12 个月的纸面 Lift 与可交易 Lift。纸面 Lift = 模型 Top-2 组合月收益 ÷ 全市场平均月收益(收盘打分、收盘价成交、无摩擦的平行世界);可交易 Lift = 同一组合次日开盘成交、扣双边成本 0.3%、跳过一字板后的真实倍数(随机基准 = 1.0)。生成规则:纸面 = 1.36 + 0.16·N(0,1);可交易 = 纸面 − 0.50 + 0.13·N(0,1)——排序能力真实存在(纸面稳定 > 1),但入场透支吃掉了全部超额)把两个世界的差距摆在一起——每个月的纸面 Lift 与可交易 Lift,虚线是随机基准 1.0:
纸面 Lift 月月站在 1.0 之上,均值 1.376——模型是真会排序,这不是运气;可交易 Lift 的均值却只有 0.842,大半时间在随机基准之下。两条曲线之间的落差,就是「排序能力」与「赚钱能力」之间那道由入场溢价和摩擦构成的鸿沟。数据是合成的(生成规则见 meta),落差刻意做得清晰;真实研究里这道鸿沟可以更隐蔽——这正是它危险的地方:只看纸面指标的研究,连自己已经掉沟里了都不知道。
伪代码:训练与双轨评估
Section titled “伪代码:训练与双轨评估”输入:每日截面特征 X[t][i]、下期收益 r[t][i](严格时间切分,第 6 章封条)输出:排序模型,以及「纸面」与「可交易」两套评估
第一步:构造训练样本(每个交易日 = 一个查询组) 对每个训练日 t: 组内股票两两配对 (i, j): 若 r[t][i] > r[t][j]:样本标签 =「i 应排在 j 前」
第二步:训练 最小化成对损失 Σ log(1 + e^{−(s_i − s_j)})(梯度提升树等实现均可) 训练只用 t ≤ T0 的数据;样本外数据贴封条
第三步:纸面评估(平行世界) 样本外每日:按分数取 Top-K,按当日收盘价「成交」 计算月度 NDCG / Lift —— 回答「排序准不准」
第四步:可交易评估(现实世界) 同样的 Top-K,但:次日开盘成交、一字板跳过、双边成本照扣 计算真实组合收益,与随机等权组合对照 —— 回答「赚不赚钱」
判读: 纸面好看 + 可交易跑输随机 → 指标错配 → 模型学到的是「谁刚涨完」而不是「谁接下来涨」 → 处方:改造特征(剔除已透支的涨幅)、换入场规则、或放弃该信号第三步和第四步必须同屏报告。只交第三步成绩单的研究,默认按隐瞒第四步处理。
- 特征泄漏。打分时刻还不知道的信息混进特征(用当日收盘后才有的大单数据给当日收盘打分、用报告期而非披露日的财务数据),NDCG 会漂亮得离谱——那是未来函数换了件马甲(第 1 章、第 6 章的老朋友)。
- 只报 NDCG / Lift,不报可交易 PnL。本章的主罪。会议论文可以只比排序指标,真金白银的研究不行。
- Top-K 的 K 是挑的。K=3 最好就报 K=3,K=10 就当没跑过——和第 6 章「只报最好的参数」是同一种骗局。K 与调仓频率要预先定死。
- 忽视「强票买不到」。涨停排队、流动性枯竭,回测里假设全能按收盘价成交。下一章把这个坑单独讲一整节。
- 把排序当预测用。排序模型只输出相对强弱,不含绝对方向——熊市里它选出的「最强」也只是最抗跌的,照样跌。先过上一章的门禁(环境对不对),再谈排序(环境里谁强)。
排序的教训是「回测世界内部要自洽」;置换、门禁、排序讲完,还剩最后两块拼图:怎么检验「检验本身」没有骗你,以及回测世界与真实 A 股之间还隔着什么。下一章先解决前者——把第 6 章的防骗清单升级成重武器。