<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>LightGBM on 远思码技术实验室</title><link>https://yuansima.com/tags/lightgbm/</link><description>Recent content in LightGBM on 远思码技术实验室</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Wed, 21 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://yuansima.com/tags/lightgbm/index.xml" rel="self" type="application/rss+xml"/><item><title>LightGBM 截面选股：把因子流水线变成预测模型</title><link>https://yuansima.com/p/lightgbm-cross-sectional-stock-prediction/</link><pubDate>Wed, 21 Oct 2026 20:20:00 +0800</pubDate><guid>https://yuansima.com/p/lightgbm-cross-sectional-stock-prediction/</guid><description>&lt;p&gt;&lt;em&gt;远思为径，码动价值。欢迎来到远思码技术实验室的最新技术实践记录。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://yuansima.com/p/ma-momentum-rotation-strategy/" &gt;上一篇文章&lt;/a&gt;的策略是&amp;quot;人工写规则&amp;quot;，参数换了就要重跑一遍。机器学习选股的思路不同：&lt;strong&gt;把多个因子交给模型，让它自己学加权方式&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;听起来很美，但实验室第一次做的时候，IC 跑到了 0.15（这个数字在 A 股日频里强得不像真的），回测年化 60%。查了两天才发现——&lt;strong&gt;标签用到了未来 5 天的收盘价，而特征里的换手率是&amp;quot;未来 5 天平均换手&amp;quot;&lt;/strong&gt;。这不是机器学习，这是拿着答案考试。&lt;/p&gt;
&lt;p&gt;这篇把正确的流程完整走一遍，重点标出那些&amp;quot;看起来没问题、其实在泄露&amp;quot;的环节。&lt;/p&gt;
&lt;h2 id="一问题定义截面回归不是时间序列预测"&gt;一、问题定义：截面回归，不是时间序列预测
&lt;/h2&gt;&lt;p&gt;先说清楚我们在预测什么：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;对每个交易日 t，输入当期所有股票的因子截面，输出每只股票&lt;strong&gt;未来 5 个交易日收益的横截面排名&lt;/strong&gt;。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;为什么是排名而不是绝对收益？因为 A 股有系统性涨跌（beta），模型容易把&amp;quot;市场要涨&amp;quot;学成&amp;quot;所有股票都好&amp;quot;。&lt;strong&gt;去掉截面均值之后，模型只能学相对强弱&lt;/strong&gt;，这也和最终&amp;quot;买前 N 只&amp;quot;的决策方式一致。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ml/labels.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; numpy &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; np
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pandas &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;forward_return&lt;/span&gt;(close: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame, horizon: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;未来 horizon 日收益。注意：这里的时间箭头是指向未来的，永远只能用于标签。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; close&lt;span style="color:#f92672"&gt;.&lt;/span&gt;shift(&lt;span style="color:#f92672"&gt;-&lt;/span&gt;horizon) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; close &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cross_section_rank&lt;/span&gt;(y: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;截面排序归一化到 [-1, 1]，比 z-score 更抗极端值。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; y&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rank(axis&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, pct&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="二特征因子池--截面处理"&gt;二、特征：因子池 + 截面处理
&lt;/h2&gt;&lt;p&gt;特征直接复用&lt;a class="link" href="https://yuansima.com/p/quant-factor-engineering/" &gt;因子工程&lt;/a&gt;里落库的 &lt;code&gt;factor_value&lt;/code&gt; 表，再补几个：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;组别&lt;/th&gt;
 &lt;th&gt;特征&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;动量&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;mom_5 / mom_20 / mom_60 / mom_120&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;多尺度动量&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;反转&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;rev_5 / rev_20&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;短期反转&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;波动&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;vol_20 / vol_60 / max_dd_60&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;风险特征&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;流动性&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;turn_20 / amount_20 / amihud&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;换手与冲击成本代理&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;估值&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pe_ttm / pb / ps_ttm&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;财务因子（低频，按季更新）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;行业&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;industry&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;类别特征，LightGBM 原生支持&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;关键纪律：&lt;strong&gt;所有特征都必须在截面上做稳健标准化&lt;/strong&gt;，并且标准化只用当日截面信息（不用全样本均值），这样在任何时点重算都一致。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ml/features.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; __future__ &lt;span style="color:#f92672"&gt;import&lt;/span&gt; annotations
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; numpy &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; np
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pandas &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;load_factor_panel&lt;/span&gt;(factor_names: list[str], start: str, end: str) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;从 factor_value 取宽表并堆成 (trade_date, symbol) 长表。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;from&lt;/span&gt; sqlalchemy &lt;span style="color:#f92672"&gt;import&lt;/span&gt; create_engine
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; engine &lt;span style="color:#f92672"&gt;=&lt;/span&gt; create_engine(&lt;span style="color:#e6db74"&gt;&amp;#34;postgresql+psycopg2://quant:quant@127.0.0.1:5432/quant&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sql &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; SELECT trade_date, symbol, factor_name, z
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; FROM factor_value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; WHERE factor_name = ANY(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(names)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; AND trade_date BETWEEN &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(start)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; AND &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%(end)s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; df &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;read_sql(sql, engine, params&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;names&amp;#34;&lt;/span&gt;: factor_names, &lt;span style="color:#e6db74"&gt;&amp;#34;start&amp;#34;&lt;/span&gt;: start, &lt;span style="color:#e6db74"&gt;&amp;#34;end&amp;#34;&lt;/span&gt;: end})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pivot_table(index&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;trade_date&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;symbol&amp;#34;&lt;/span&gt;], columns&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;factor_name&amp;#34;&lt;/span&gt;, values&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;z&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;add_industry_and_label&lt;/span&gt;(panel: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame, close: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; industry: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame, horizon: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;拼上行业标签与截面排序标签，并丢弃最后 horizon 天（没有未来收益）。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; y &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cross_section_rank(forward_return(close, horizon))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; long &lt;span style="color:#f92672"&gt;=&lt;/span&gt; y&lt;span style="color:#f92672"&gt;.&lt;/span&gt;stack()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rename(&lt;span style="color:#e6db74"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;reset_index()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; long&lt;span style="color:#f92672"&gt;.&lt;/span&gt;columns &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;trade_date&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;symbol&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ind &lt;span style="color:#f92672"&gt;=&lt;/span&gt; industry&lt;span style="color:#f92672"&gt;.&lt;/span&gt;stack()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rename(&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;reset_index()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ind&lt;span style="color:#f92672"&gt;.&lt;/span&gt;columns &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;trade_date&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;symbol&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; out &lt;span style="color:#f92672"&gt;=&lt;/span&gt; panel&lt;span style="color:#f92672"&gt;.&lt;/span&gt;reset_index()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;merge(long, on&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;trade_date&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;symbol&amp;#34;&lt;/span&gt;], how&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;inner&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; out &lt;span style="color:#f92672"&gt;=&lt;/span&gt; out&lt;span style="color:#f92672"&gt;.&lt;/span&gt;merge(ind, on&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;trade_date&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;symbol&amp;#34;&lt;/span&gt;], how&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;left&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 行业标签在每个截面内部统一成 category，未知记为 &amp;#34;UNKNOWN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; out[&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; out[&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fillna(&lt;span style="color:#e6db74"&gt;&amp;#34;UNKNOWN&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;astype(&lt;span style="color:#e6db74"&gt;&amp;#34;category&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; out&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dropna(subset&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="三时序切分purged-与-embargo"&gt;三、时序切分：purged 与 embargo
&lt;/h2&gt;&lt;p&gt;这是整篇文章最重要的部分。机器学习课教你 K-Fold 随机切分，&lt;strong&gt;在量化里这是灾难&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;标签是未来 5 日收益，t 日的样本和 t+3 日的样本标签区间重叠 → 训练集和验证集&amp;quot;共享答案&amp;quot;；&lt;/li&gt;
&lt;li&gt;相邻交易日的特征高度自相关 → 模型只要记住&amp;quot;这几天&amp;quot;，就能在验证集上蒙对。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;实验室用的是&lt;strong&gt;按时间切分的 purged walk-forward + embargo&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ml/split.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; __future__ &lt;span style="color:#f92672"&gt;import&lt;/span&gt; annotations
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pandas &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;purged_time_split&lt;/span&gt;(dates: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series, n_splits: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, embargo: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; list[tuple]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;按时间切成 n_splits 段，训练集与验证集之间留 embargo 天缓冲。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; 返回 [(train_mask, valid_mask), ...]，mask 是与 dates 同长度的布尔数组。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; uniq &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sorted(dates&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unique())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fold &lt;span style="color:#f92672"&gt;=&lt;/span&gt; len(uniq) &lt;span style="color:#f92672"&gt;//&lt;/span&gt; (n_splits &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; idx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series(range(len(uniq)), index&lt;span style="color:#f92672"&gt;=&lt;/span&gt;uniq)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; splits &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; k &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, n_splits &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; train_end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; k &lt;span style="color:#f92672"&gt;*&lt;/span&gt; fold
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; valid_start &lt;span style="color:#f92672"&gt;=&lt;/span&gt; train_end &lt;span style="color:#f92672"&gt;+&lt;/span&gt; embargo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; valid_end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; valid_start &lt;span style="color:#f92672"&gt;+&lt;/span&gt; fold
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; valid_end &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; len(uniq):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; train_days &lt;span style="color:#f92672"&gt;=&lt;/span&gt; set(uniq[:train_end])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; valid_days &lt;span style="color:#f92672"&gt;=&lt;/span&gt; set(uniq[valid_start:valid_end])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; splits&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append((dates&lt;span style="color:#f92672"&gt;.&lt;/span&gt;isin(train_days), dates&lt;span style="color:#f92672"&gt;.&lt;/span&gt;isin(valid_days)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; splits
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;embargo=5&lt;/code&gt; 的含义：训练集最后 5 天的样本，其标签区间与验证集第一天重叠，必须&lt;strong&gt;整体切除&lt;/strong&gt;。少了这一步，验证集 IC 能凭空高 30%。&lt;/p&gt;
&lt;h2 id="四训练lightgbm"&gt;四、训练：LightGBM
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ml/train.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; __future__ &lt;span style="color:#f92672"&gt;import&lt;/span&gt; annotations
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; lightgbm &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; lgb
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; numpy &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; np
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pandas &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FEATURES &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;mom_5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;mom_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;mom_60&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;mom_120&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rev_5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rev_20&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;vol_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;vol_60&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;max_dd_60&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;turn_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;amount_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;amihud&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PARAMS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; dict(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; objective&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;regression_l2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; metric&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;l2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; learning_rate&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.03&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; num_leaves&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;15&lt;/span&gt;, &lt;span style="color:#75715e"&gt;# 别太大，截面样本噪声很重&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; min_data_in_leaf&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; feature_fraction&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.75&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bagging_fraction&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.8&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bagging_freq&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lambda_l2&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;5.0&lt;/span&gt;, &lt;span style="color:#75715e"&gt;# 强正则，防止记住某几天&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_depth&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; verbosity&lt;span style="color:#f92672"&gt;=-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; seed&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;42&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; num_threads&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;train_model&lt;/span&gt;(df: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame, train_mask, valid_mask) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; lgb&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Booster:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; X_tr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loc[train_mask, FEATURES &lt;span style="color:#f92672"&gt;+&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; y_tr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loc[train_mask, &lt;span style="color:#e6db74"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;to_numpy()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; X_va &lt;span style="color:#f92672"&gt;=&lt;/span&gt; df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loc[valid_mask, FEATURES &lt;span style="color:#f92672"&gt;+&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; y_va &lt;span style="color:#f92672"&gt;=&lt;/span&gt; df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loc[valid_mask, &lt;span style="color:#e6db74"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;to_numpy()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dtrain &lt;span style="color:#f92672"&gt;=&lt;/span&gt; lgb&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Dataset(X_tr, label&lt;span style="color:#f92672"&gt;=&lt;/span&gt;y_tr, categorical_feature&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;], free_raw_data&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dvalid &lt;span style="color:#f92672"&gt;=&lt;/span&gt; lgb&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Dataset(X_va, label&lt;span style="color:#f92672"&gt;=&lt;/span&gt;y_va, categorical_feature&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;], reference&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dtrain)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; booster &lt;span style="color:#f92672"&gt;=&lt;/span&gt; lgb&lt;span style="color:#f92672"&gt;.&lt;/span&gt;train(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PARAMS,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dtrain,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; num_boost_round&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; valid_sets&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[dtrain, dvalid],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; valid_names&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;train&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;valid&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; callbacks&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[lgb&lt;span style="color:#f92672"&gt;.&lt;/span&gt;early_stopping(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, verbose&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;), lgb&lt;span style="color:#f92672"&gt;.&lt;/span&gt;log_evaluation(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; booster
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;几个参数选择的理由，比参数本身更值得记：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;num_leaves=15 + max_depth=5 + lambda_l2=5&lt;/code&gt;&lt;/strong&gt;：截面数据的信噪比极低（IC 0.03 就算不错），复杂模型几乎必然过拟合到某几段行情；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;min_data_in_leaf=200&lt;/code&gt;&lt;/strong&gt;：一天全市场几千只股票，叶子太小就等于记住个股；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;early_stopping&lt;/code&gt; 只看验证集&lt;/strong&gt;，测试集从头到尾不参与训练决策。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="五评价不看-r只看-ic-与分层"&gt;五、评价：不看 R²，只看 IC 与分层
&lt;/h2&gt;&lt;p&gt;回归任务的 &lt;code&gt;R²&lt;/code&gt; 在这里毫无意义（0.01 也算&amp;quot;显著&amp;quot;）。真正的评价指标还是那三个：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ml/evaluate.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; numpy &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; np
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pandas &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; scipy.stats &lt;span style="color:#f92672"&gt;import&lt;/span&gt; spearmanr
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;predict_panel&lt;/span&gt;(booster, df: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pred &lt;span style="color:#f92672"&gt;=&lt;/span&gt; booster&lt;span style="color:#f92672"&gt;.&lt;/span&gt;predict(df[FEATURES &lt;span style="color:#f92672"&gt;+&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;]])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series(pred, index&lt;span style="color:#f92672"&gt;=&lt;/span&gt;df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;index)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ic_by_date&lt;/span&gt;(df: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame, pred: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series, label_col: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tmp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;assign(pred&lt;span style="color:#f92672"&gt;=&lt;/span&gt;pred)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ic &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tmp&lt;span style="color:#f92672"&gt;.&lt;/span&gt;groupby(&lt;span style="color:#e6db74"&gt;&amp;#34;trade_date&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;apply(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;lambda&lt;/span&gt; g: spearmanr(g[&lt;span style="color:#e6db74"&gt;&amp;#34;pred&amp;#34;&lt;/span&gt;], g[label_col])&lt;span style="color:#f92672"&gt;.&lt;/span&gt;statistic
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; len(g) &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; np&lt;span style="color:#f92672"&gt;.&lt;/span&gt;nan,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; include_groups&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ic&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dropna()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ic_stats&lt;/span&gt;(ic: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; dict:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;IC均值&amp;#34;&lt;/span&gt;: round(float(ic&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mean()), &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;ICIR&amp;#34;&lt;/span&gt;: round(float(ic&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mean() &lt;span style="color:#f92672"&gt;/&lt;/span&gt; ic&lt;span style="color:#f92672"&gt;.&lt;/span&gt;std(ddof&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)), &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;IC&amp;gt;0占比&amp;#34;&lt;/span&gt;: round(float((ic &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mean()), &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;IC衰减(后20%)&amp;#34;&lt;/span&gt;: round(float(ic&lt;span style="color:#f92672"&gt;.&lt;/span&gt;iloc[&lt;span style="color:#f92672"&gt;-&lt;/span&gt;len(ic) &lt;span style="color:#f92672"&gt;//&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;:]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mean()), &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;long_short_backtest&lt;/span&gt;(df: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame, pred: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series, top_pct: float &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;按预测分位做多空组合，返回每日收益（未计成本）。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tmp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;assign(pred&lt;span style="color:#f92672"&gt;=&lt;/span&gt;pred)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; daily &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; date, g &lt;span style="color:#f92672"&gt;in&lt;/span&gt; tmp&lt;span style="color:#f92672"&gt;.&lt;/span&gt;groupby(&lt;span style="color:#e6db74"&gt;&amp;#34;trade_date&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; max(&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;, int(len(g) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; top_pct))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; g &lt;span style="color:#f92672"&gt;=&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sort_values(&lt;span style="color:#e6db74"&gt;&amp;#34;pred&amp;#34;&lt;/span&gt;, ascending&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; daily&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append((date, g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head(n)[&lt;span style="color:#e6db74"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mean() &lt;span style="color:#f92672"&gt;-&lt;/span&gt; g&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tail(n)[&lt;span style="color:#e6db74"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mean()))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; s &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Series(dict(daily))&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sort_index()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实验室的实测水平（示例，2018-2025 的日频、5 日持有）：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;指标&lt;/th&gt;
 &lt;th&gt;数值&lt;/th&gt;
 &lt;th&gt;评价&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;RankIC 均值&lt;/td&gt;
 &lt;td&gt;0.042&lt;/td&gt;
 &lt;td&gt;可用（&amp;gt; 0.03）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ICIR&lt;/td&gt;
 &lt;td&gt;0.31&lt;/td&gt;
 &lt;td&gt;弱但稳定&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;IC &amp;gt; 0 占比&lt;/td&gt;
 &lt;td&gt;0.58&lt;/td&gt;
 &lt;td&gt;尚可&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;多空年化（未计成本）&lt;/td&gt;
 &lt;td&gt;16.4%&lt;/td&gt;
 &lt;td&gt;与 IC 量级相符&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;多空最大回撤&lt;/td&gt;
 &lt;td&gt;-12.7%&lt;/td&gt;
 &lt;td&gt;可接受&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;注意这里的 IC 只有 0.042，远低于我第一版的 0.15。&lt;strong&gt;0.15 不是好消息，是报警信号&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="六过拟合诊断三个必查项"&gt;六、过拟合诊断：三个必查项
&lt;/h2&gt;&lt;h3 id="61-特征重要性是否稳定"&gt;6.1 特征重要性是否稳定
&lt;/h3&gt;&lt;p&gt;同一套流程在 2020-2022 和 2023-2025 两段数据上分别训练，如果两次的 top 特征完全不同，说明模型学的是噪声。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;top_features&lt;/span&gt;(booster, k: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; list[str]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; imp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; booster&lt;span style="color:#f92672"&gt;.&lt;/span&gt;feature_importance(importance_type&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;gain&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; names &lt;span style="color:#f92672"&gt;=&lt;/span&gt; booster&lt;span style="color:#f92672"&gt;.&lt;/span&gt;feature_name()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; order &lt;span style="color:#f92672"&gt;=&lt;/span&gt; np&lt;span style="color:#f92672"&gt;.&lt;/span&gt;argsort(imp)[::&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][:k]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; [names[i] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#f92672"&gt;in&lt;/span&gt; order]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实验室两次训练的 top 特征都是 &lt;code&gt;rev_5 / mom_20 / turn_20 / vol_20&lt;/code&gt;，只有排序在变——这才叫稳定。&lt;/p&gt;
&lt;h3 id="62-分年度-ic-是否衰减"&gt;6.2 分年度 IC 是否衰减
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;by_year &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ic&lt;span style="color:#f92672"&gt;.&lt;/span&gt;groupby(ic&lt;span style="color:#f92672"&gt;.&lt;/span&gt;index&lt;span style="color:#f92672"&gt;.&lt;/span&gt;str[:&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;])&lt;span style="color:#f92672"&gt;.&lt;/span&gt;agg([&lt;span style="color:#e6db74"&gt;&amp;#34;mean&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;count&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果 2024 年之后 IC 突然接近 0，那不是&amp;quot;市场变了&amp;quot;，更可能是因子被交易过度拥挤了。&lt;strong&gt;任何因子都有生命周期&lt;/strong&gt;，要接受它。&lt;/p&gt;
&lt;h3 id="63-shap-方向是否反常识"&gt;6.3 SHAP 方向是否反常识
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; shap
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;explain_one_day&lt;/span&gt;(booster, df: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame, date: str, top_n: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; day &lt;span style="color:#f92672"&gt;=&lt;/span&gt; df[df[&lt;span style="color:#e6db74"&gt;&amp;#34;trade_date&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; date]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; explainer &lt;span style="color:#f92672"&gt;=&lt;/span&gt; shap&lt;span style="color:#f92672"&gt;.&lt;/span&gt;TreeExplainer(booster)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; explainer&lt;span style="color:#f92672"&gt;.&lt;/span&gt;shap_values(day[FEATURES &lt;span style="color:#f92672"&gt;+&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;]])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; shap_df &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame(sv, columns&lt;span style="color:#f92672"&gt;=&lt;/span&gt;FEATURES &lt;span style="color:#f92672"&gt;+&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;], index&lt;span style="color:#f92672"&gt;=&lt;/span&gt;day&lt;span style="color:#f92672"&gt;.&lt;/span&gt;index)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; shap_df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;abs()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mean()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sort_values(ascending&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head(top_n)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果 SHAP 告诉你&amp;quot;换手率越低越好&amp;quot;，而你去查因子定义发现写反了符号——&lt;strong&gt;这类 bug 在 IC 评价里看不出来，但会在实盘里原形毕露&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="七从预测到组合别忽略这一步"&gt;七、从预测到组合：别忽略这一步
&lt;/h2&gt;&lt;p&gt;模型输出的是相对分数，不是权重。从分数到组合中间还有几件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;截断极值&lt;/strong&gt;：按预测分位取前 10%，而不是简单取 top 50 只（防止小盘股霸榜）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;行业约束&lt;/strong&gt;：行业偏离不超过 3%，否则模型会集中押注某几个行业；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;权重平滑&lt;/strong&gt;：用分数排名加权（&lt;code&gt;w ∝ rank&lt;/code&gt;），比等权更能利用信息，但换手更高；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在池内选股&lt;/strong&gt;：只在沪深 300 / 中证 500 成分股里选，避免流动性黑洞。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些环节对最终净值的影响，往往比换一个更强的模型更大。&lt;/p&gt;
&lt;h2 id="八踩坑清单"&gt;八、踩坑清单
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;标签泄露&lt;/strong&gt;：&lt;code&gt;forward_return&lt;/code&gt; 只能用于 &lt;code&gt;y&lt;/code&gt;，如果它不小心进入特征表（比如按 symbol merge 时列名撞车），IC 会好到不真实。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;复权口径不一致&lt;/strong&gt;：特征用前复权、标签用未复权，除权日会凭空出现 -30% 的标签。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;行业分类会变&lt;/strong&gt;：申万行业每年调整，历史行业标签要用&lt;strong&gt;当期&lt;/strong&gt;分类，不能用今天的分类回填。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;停牌股&lt;/strong&gt;：停牌期间没有任何成交，标签却是&amp;quot;未来 5 日收益&amp;quot;，必须按可交易性把样本剔除，否则模型会学到&amp;quot;停牌股复牌后大涨&amp;quot;的伪规律。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;groupby.apply&lt;/code&gt; 的 &lt;code&gt;include_groups&lt;/code&gt;&lt;/strong&gt;：pandas 2.2 之后行为变了，老代码会静默丢列，IC 计算直接错位。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="结语"&gt;结语
&lt;/h2&gt;&lt;p&gt;LightGBM 这条路的上限是清楚的：&lt;strong&gt;它能把多个弱因子合成一个稍强的信号，但不会凭空创造 alpha&lt;/strong&gt;。IC 从单因子的 0.03 提到 0.042，靠的是模型对非线性与交互的捕捉，以及行业信息的利用。&lt;/p&gt;
&lt;p&gt;如果还想往上走，就要引入序列结构——这正是下一篇的主题：用 &lt;a class="link" href="https://yuansima.com/p/tft-stgnn-quant-rocm/" &gt;TFT 与 ST-GNN&lt;/a&gt; 捕捉时序与截面图结构。那条路的坑，比 LightGBM 多得多。&lt;/p&gt;
&lt;p&gt;远思为径，码动价值。模型不会让你赚钱，只会放大你流程里的漏洞。&lt;/p&gt;</description></item></channel></rss>