策略 V2 定版
沪深300 成分股 · 2018-01 ~ 2026-09(105 个月)· 全部因子 point-in-time · 生成于 2026-10-07
一句话:我原本以为问题是「权重没调好」——7:3 确实偏保守,但把 V3 腿单独拆开看,真正的病根是「盈利增长」这个因子在参与排序。把它从「占 50% 权重参与打分」降级为「只做剔除门槛(净利同比≤0 直接出局)」后,V3 腿年化从 6.99% 升到 12.19%,同时波动从 21.94% 降到 17.40%、回撤从 24.64% 降到 15.55%。这不是「多担风险换收益」,是效率提升。这一条比调权重大得多。
头部对比(总收益口径 = 分红再投,税前)
| 指标 | V1 旧版 | V2 定版 | 变化 |
| 年化收益率 | 11.41% | 17.37% | +5.96pp |
| 年化波动率 | 19.99% | 17.19% | -2.81pp |
| 夏普比率 | 0.471 | 0.894 | +0.424 |
| 最大回撤 | 24.96% | 20.17% | -4.78pp |
| CVaR 5%(最差 5% 月份的均值) | -10.00% | -8.24% | +1.76pp |
| 105 个月累计收益 | 157.38% | 306.03% | +148.65pp |
对照:同期沪深300 年化 0.89%、回撤 39.92%。V2 的累计收益是 306.03%,HS300 是 8.11%。以上为未扣交易成本的毛收益,扣成本后见第五节。
一、V1 → V2 具体改了什么
| 项目 | V1(现行) | V2(定版) |
| V3 选股法 | 0.5 × 股息率分位 + 0.5 × 盈利增长分位 排序 | 先剔除「净利同比 ≤ 0」,再纯按股息率排序 |
| V3 持股数 | Top 7 等权 | Top 7 等权(不变) |
| 成长仓持股数 | Top 3 等权(单票 10%) | Top 5 等权(单票 8%) |
| 权重 V3 : 成长 | 70 : 30 | 60 : 40 |
| 调仓频率 / 择时 | 每月 1 次,无择时 | 每月 1 次,无择时(不变) |
为什么要这么改 V3:原设计引入「盈利增长」是为了规避高股息陷阱(躲开那种股息高但利润在崩的股票)——这个初衷完全正确。错在让它参与评分排序,那等于主动去追高增长。实测双因子排序 6.99%,既输给只做门槛的 12.19%,也输给完全不管增长的纯股息 9.49%。「做门槛」≠「做排序权重」,这是本次最核心的一条。
二、V3 腿:三种选股法(同一批数据、同一 105 个月)
| V3 选股法 | 年化 | 波动 | 夏普 | 最大回撤 | CVaR 5% | 月胜率 |
| 双因子 0.5/0.5(V1) | +6.99% | 21.94% | 0.227 | 24.64% | -9.15% | 49.5% |
| 纯股息率排序 | +9.49% | 18.67% | 0.401 | 16.87% | -8.46% | 50.5% |
| 盈利>0 做门槛 + 股息率(V2) | +12.19% | 17.40% | 0.585 | 15.55% | -7.50% | 53.3% |
注意 filtered 是三项里唯一在收益、波动、回撤、尾部四个维度同时占优的:年化比 V1 高 5.20pp,回撤低 9.09pp。纯股息排序也一样改善,但幅度小一圈 —— 说明「盈利门槛」这个动作额外贡献了约 2.70pp 年化,不是多余的装饰。
三、消融实验:每个改动各值多少(不是把最好的拼起来)
3 种 V3 选股法 × 2 种成长持股数 × 2 个权重 = 12 个组合全跑了
| V3 选股法 | 成长只数 | 权重 | 年化 | 波动 | 夏普 | 最大回撤 |
| 双因子(V1) | 3 | 70:30 | +11.41% | 19.99% | 0.471 | 24.96% |
| 双因子(V1) | 3 | 50:50 | +13.74% | 21.84% | 0.537 | 25.40% |
| 双因子(V1) | 5 | 70:30 | +12.45% | 19.66% | 0.532 | 28.36% |
| 双因子(V1) | 5 | 50:50 | +15.58% | 21.05% | 0.645 | 30.95% |
| 纯股息 | 3 | 70:30 | +13.21% | 17.64% | 0.635 | 15.74% |
| 纯股息 | 3 | 50:50 | +15.04% | 20.39% | 0.639 | 19.33% |
| 纯股息 | 5 | 70:30 | +14.34% | 16.79% | 0.735 | 17.49% |
| 纯股息 | 5 | 50:50 | +16.99% | 19.05% | 0.787 | 23.57% |
| 盈利门槛+股息 | 3 | 70:30 | +15.13% | 16.85% | 0.779 | 14.99% |
| 盈利门槛+股息 | 3 | 50:50 | +16.42% | 19.97% | 0.722 | 18.59% |
| 盈利门槛+股息 | 5 | 70:30 | +16.25% | 16.16% | 0.882 | 17.03% |
| 盈利门槛+股息 | 5 | 50:50 | +18.36% | 18.80% | 0.870 | 23.25% |
| V2 定版 盈利门槛+股息 | 5 | 60:40 | +17.37% | 17.19% | 0.894 | 20.17% |
两个改动的性质完全不同(下面把 4 种「持股数×权重」组合的区间都列出来,不挑好看的那一行):
①「V3 改盈利门槛」全部 4 组都改善:年化 +2.68 ~ +3.80pp,回撤 -11.33 ~ -6.81pp(负=变小)。这是修复。
②「成长 3→5 只」年化 +1.12 ~ +1.94pp,但回撤 全部 4 组都恶化 +2.04 ~ +4.66pp。这是稀释,是有代价的。
我最后仍保留 5 只,理由是「不把 40% 的仓位压在 3 张彩票上」——但它是权衡,不是白捡。
四、权重:60:40 是怎么选出来的
| 配置 | 年化 | 波动 | 夏普 | 最大回撤 | CVaR 5% | 滚动12月胜率 | 剔除最好3月后 |
| V1 现行 7:3 | +11.41% | 19.99% | 0.471 | 24.96% | -10.00% | 84.0% | +6.82% |
| V2 候选 70:30 | +16.25% | 16.16% | 0.882 | 17.03% | -7.71% | 96.8% | +11.47% |
| V2 定版 60:40 | +17.37% | 17.19% | 0.894 | 20.17% | -8.24% | 95.7% | +12.32% |
| V2 候选 50:50 | +18.36% | 18.80% | 0.870 | 23.25% | -8.90% | 89.4% | +12.90% |
60:40 胜出的依据
- 6 个「窗口 × 口径」组合里,60:40 在 3 个是夏普最高的(程序逐个比较,非人工挑选)。
- 它在三个窗口、两个口径上都单调落在 70:30 与 50:50 之间,从不跑偏 —— 是平滑曲线上的点,不是噪声尖峰。
- 全窗口 bootstrap:总收益口径 P = 0.967、价格口径 P = 0.960,是三个候选里唯一两个口径都过 0.95 的。
- 滚动 12 月胜率 95.7%,最差滚动 12 个月 -12.3%。
我承认的问题
- 60:40 是我在看到 70:30 与 50:50 结果之后才补进去的 —— 严格说有事后挑选成分,这一点必须写明。
- 若只要求「全窗口 bootstrap 显著」,50:50 的 P 其实更高(0.977),而且它的 95%CI 下界 +0.09pp 已经跨过 0,60:40 的下界是 -0.47pp。单看 bootstrap,50:50 反而更硬。
- 我选 60:40 而不是 50:50 的理由是尾部:50:50 回撤 23.25%、最差滚动12月 -15.2%,比 60:40 的 20.17% / -12.3% 明显更差,滚动12月胜率也从 95.7% 掉到 89.4%。你要的「避免极端波动」,我把它排在 bootstrap 显著性之前。
五、稳健性:多窗口 × 多口径 × 换手成本
| 窗口 / 口径 | V1 年化 | V2 年化 | 差 | V1 夏普 | V2 夏普 | V2 回撤 | P(优于V1) |
| 全窗口 总收益 | +11.41% | +17.37% | +5.96% | 0.471 | 0.894 | 20.17% | 0.967 |
| 全窗口 价格 | +8.18% | +14.19% | +6.01% | 0.307 | 0.711 | 21.05% | 0.960 |
| 子样本A 总收益 | +16.24% | +18.67% | +2.43% | 0.673 | 0.889 | 20.17% | 0.652 |
| 子样本A 价格 | +13.74% | +16.32% | +2.58% | 0.555 | 0.769 | 21.05% | 0.676 |
| 子样本B 总收益 | +6.96% | +14.25% | +7.30% | 0.299 | 0.912 | 9.29% | 0.971 |
| 子样本B 价格 | +3.17% | +10.38% | +7.21% | 0.070 | 0.627 | 9.90% | 0.973 |
bootstrap:block=6 月、4000 次重抽月度收益差;P > 0.95 视为显著。子样本 A(2018-2022,54 个月)全部不显著 —— 样本短、波动大,本来就难显著;子样本 B(2022-2026)显著。
换手成本:这一节是算出来的,不是估的
| 口径 | V1 旧版 | V2 定版 |
| 月均单边换手率 | 23.3% | 25.2% |
| 年换手(月均×12) | 279% | 303% |
| 毛年化 | 11.41% | 17.37% |
| 成本拖累 | -0.31% | -0.35% |
| 净年化(扣佣金0.025% + 卖出印花税0.05%) | 11.10% | 17.02% |
| 净夏普 | 0.455 | 0.874 |
结论没被成本吃掉:V2 持仓更分散、成长腿权重更高,月均换手从 23.3% 升到 25.2%,成本拖累多了 0.04pp。但扣完成本,V2 相对 V1 的优势仍有 5.92pp 年化(毛差 5.96pp),净夏普 0.455 → 0.874。这个改动对交易摩擦不敏感。
六、反对意见与代价(这是扣分项,不是免责声明)
1. 最新的 2026 年 1-9 月,V2 打不过 V1 —— 这是真金白银的代价。V1 累计 +9.04%,V2 累计 +1.52%,差 7.52pp。原因很具体:成长仓从 3 只扩到 5 只之后,今年成长腿只有 +5.36%,而 3 只版本是 +36.20% —— 2026 年就是少数几只在涨,分散化直接稀释掉了。分散是买保险,保险费在好年份必须付。同期沪深300 -5.88%,两版都跑赢,但 V2 今年确实是输的那个。
2. 收益仍高度集中在少数月份。剔除最好的 3 个月后,V2 年化从 +17.37% 掉到 +12.32%;V1 同期从 +11.41% 掉到 +6.82%。这说明两个版本的超额都靠少数暴涨月支撑,不是稳定现金流。
3. 全部结论仍是样本内。同一段历史既用来发现「双因子拖累」,也用来验证「过滤法更好」,中间没有任何 out-of-sample 环节。数据就这么多,我没有硬造一个样本外。建议先小仓位跑 6 个月,用真实成交价格核对。
4.「净利同比 > 0」依赖东财单一口径(PNP_YOY_RATIO,按披露日 ≤ 选股日取最新一期),与 V3 原本自算的季度同比不完全等价。过滤后平均候选池 180 只(过滤前 246 只,最少的月份仍有 143 只)—— 不至于变成小样本收割,但这个依赖是实打实的。
5. YTD 数字是独立窗口算的,和主净值曲线的尾部不完全一致。主曲线里成长仓会带着上一年底的持仓跨年,而 YTD 窗口从空仓起算,两者不能简单相减。V1 与 V2 用同一方法算,横向可比。
七、净值曲线(对数刻度)
全窗口 2018-01 ~ 2026-09
- V2 净值 4.06x V1 2.57x 沪深300 1.08x
- V2 单腿:V3 2.73x、成长 5.43x
- 价格口径(含送转、不含现金分红)V2 年化 14.19%,分红对 V2 年化的贡献约 3.18pp
2026 年 1-9 月(不作弊,单独列出来)
- V2 混合 +1.52%,V3 腿 -5.04%、成长腿 +5.36%
- V1 混合 +9.04%,V3 腿 -4.61%、成长腿 +36.20%
- 沪深300 -5.88%,最大回撤 12.49%
八、落地规则(每月 1 号照做)
- V3 底仓 60%:在沪深300 成分股中,取「过去 12 个月有实际除权派息」且「最新已披露净利同比 > 0」(
PUBLISH_DATE ≤ 调仓日)的股票,按股息率从高到低取前 7 只,等权,每只约 8.6%。
- 成长仓 40%:ADX(14) > 22 且 +DI > −DI;TRIX > 0 且 TRIX > TRMA;收盘 > BOLL 中轨(20, 2);收盘 > 上月最高;成交额 > MA(VOL, 20)。按 ADX 从高到低取前 5 只等权,每只约 8%。已持仓者只要收盘仍在 BOLL 中轨上方且 TRIX > TRMA 就继续持有,不占用新名额。
- 每月 1 号把两腿权重重置回 60 : 40。回测就是按这个口径做的,别让它漂移。
- 不加止损、不加择时、不加波动开关。我试过波动目标 18%:年化掉约 2.1pp 只换来波动降 0.9pp,在 2020-2021 那段高波动上涨里还会踏空,性价比不够,没采用。
回测中 2026-09 的持仓(PIT 同源,不是今日行情快照):V3 = sh600066, sz002027, sh600741, sz002532, sh600803, sh601166, sh600011 | 成长 = sz300394, sh605117, sh688072, sz002371, sh603259
九、数据修正记录(2026-10-07,本报告为修正后版本)
修正了三处会改变买卖结论的问题,按严重性排序。本报告所有数字已是修正后重算的结果,与同日更早发布的版本不同。
① ADX 存在 1 个月前视(最严重)
- 问题:Wilder ADX 的月份标签写错了一位。
dict[X] 里存的其实是"截至 X+1 月"的 ADX,而回测用 adx[prev] 决定 prev+1 月的持仓 —— 等于提前看了一个月的 K 线才决定要不要买。用独立实现的「截至该月」版本逐月比对确认:抽样 6 只 × 12 个月,12/12 全部吻合"上月标签=本月数据"。
- 只影响成长腿(V3 腿不用 ADX):V3 腿 12.19% 修正前后完全不变,成长腿 23.23% → 21.33%,可反证污染源只此一处。
- 影响幅度:V2 组合年化 18.15% → 17.37%(−0.78pp)、夏普 0.940 → 0.894、回撤 20.17% 不变;V1 组合 12.09% → 11.41%、夏普 0.507 → 0.471。子样本 A(2018-2021)修正前后完全一致,说明前视主要污染近年的入场时点。
- 结论没翻:修正后 V2 仍全面优于 V1(年化 +5.96pp、夏普 +0.424、回撤低 4.78pp);60:40 的 bootstrap P 值 0.967/0.960(总收益/价格口径)双口径仍 > 0.95,50:50 依然显著性更强(0.977/0.982)—— 「选 60:40 是把尾部风险排在统计显著性之前」这个取舍仍然成立。
② 实盘的复权价来源一直是脏数据
- 问题:实盘脚本原本优先读
kline_monthly_cache.json。它是分批前复权拼接的产物:在全市场 34,530 个「非除权月」样本里,只有 4.45% 的月收益与真实不复权收益吻合,重建源为 99.96%。
- 后果:例如 sz002028 在 2026-08 的真实月收益是 -10.47%,旧源写成 +17.02%(差 27.5pp)。最近半年有 35.5% 的(个股,月)收益被改写超过 1pp,成长腿的 ADX/TRIX/布林全都跑在这种序列上。
- 处理:改为由
_sina_ohlcv.json(不复权真实价)+ _div_history2.json(分红送转)现场重建,与回测引擎 hybrid_pit_core.build 完全同口径;旧缓存标记为禁止使用,仅在极端缺失时回退并打印告警。
③ 实盘的成长腿玩法和回测不是一回事
- 问题:回测是持仓延续制(满仓月刊第七章的规则),但实盘脚本写成了每月重新闯五关取 ADX 最高 N 只 —— 同一套因子,两种玩法,长期必然分叉。
- 处理:实盘的成长腿改为直接调用回测核心
run_growth,不再复制逻辑。V3 腿是无延续的逐月重选、等价于核心的 filtered 模式,已用核心历史逐月比对 32 个月,一致率 100%。
- 副作用要知情:持仓延续制有路径依赖 —— 从哪个月开始跑,会影响当前持仓名单。实盘因此锚定回测主净值曲线的起点 2017-12,保证与发布的净值同源。
局限(沿用 V1 报告,均未消除)
- 幸存者偏差:股票池是当前沪深300 成分股;2018 年被剔除的公司不在池内,两腿收益都可能被高估。
- 未扣股息红利税:总收益口径为税前分红再投,持股不足 1 年会低于此数;再投按除权价买入同一只股票,不切实际地假设零冲击成本。
- 忽略停牌、涨跌停无法成交、流动性冲击:一律假定月末收盘价可成交。
- 成长腿指标在月线上计算,实盘是日线判定,两者不完全等价。
- V2 是回测定版,不是实盘成绩。从下一个调仓日起才有实盘意义。