← 返回目录
p5.2动手⏱ 约 16 分钟

策略族谱:每个族在什么条件下失效

事后标签说"趋势跟随牛市 19%、熊市 -19%";换成交易时拿得到的标签,结论反过来——两套标签只有 50.4% 重合

🔎 最后验证 2026-07📚 来源:5 个策略族 × 8 只股票 × 2 套市场状态划分,2026-07-31 于 ECS 实测,输出见 code/outputs/🧰 qlab.engine(本课自研)、pandas 3.0.5、Python 3.12.13
为什么学这个

把五个策略族放在同一批标的、同一个引擎上跑 16 年,再按市场状态拆开。

事后划分的表符合所有人的直觉:

                     牛      震荡      熊
趋势跟随(双均线)    19.03    3.71   -19.22
买入持有            34.29    9.76   -25.91

换成交易时真能拿到的标签(只用过去数据),同一批净值曲线:

                   近6月强   近6月平   近6月弱
趋势跟随(双均线)    -2.46    3.71     7.08
买入持有            -0.88    3.94    31.82

两张表说的是反的。而它们用的是同一批净值曲线——变的只是"怎么给日子贴标签"。

原因在最后一节:两套标签的桶大小几乎一样(856 日 vs 856 日),但只有 50.4% 重合

💡 打个比方

"雨天带伞"是对的。

但天气预报说的是"明天下雨"——而事后统计说的是"那天下雨了"

用后者算出来的带伞收益率,你一天也享受不到。

一、五个族,同一个引擎

五个族都能用单标的引擎表达,所以口径严格一致(同一份数据、同一套撮合规则、同样的成本):

规则属于哪一类
趋势跟随双均线 5/20,上穿持有追随价格方向
均值回归跌破布林下轨买入,回到中轨卖出押价格回到中枢
时序动量过去 12 个月(剔除最近 1 个月)收益为正则持有追随中期方向
波动率目标目标年化波动 15%,按实测波动缩放仓位不预测方向,只管风险
买入持有全程满仓基准

全样本表现(8 只股票均值,2010-01 ~ 2026-07):

策略族年化%最大回撤%夏普成交笔数
趋势跟随(双均线)0.43-59.40.133258
均值回归(布林带)0.30-43.00.107128
时序动量(12-1)1.17-61.60.171122
波动率目标(15%)3.58-41.40.3012118
买入持有3.00-60.00.2562

四个"策略"里三个跑输买入持有。唯一跑赢的是波动率目标——而它不预测任何方向,只按实测波动缩放仓位。

它赢的方式也不是多赚:年化只高 0.58 个百分点,但最大回撤从 -60.0% 降到 -41.4%。这个族的价值在风险端,不在收益端。

⚠️ 避坑

上表是"逐股算年化、再取算术平均"的口径。换一种汇总方式,这个 +0.58 pp 会翻转。

同一批回测,如果改成"8 只票的日收益等权合成一个组合、再算年化":

汇总口径波动率目标买入持有超额
A 逐股年化再平均3.58%3.00%+0.58 pp
B 日收益等权成组合4.28%5.87%-1.59 pp

两个口径都没算错。 差别来自等权日频再平衡降低了组合波动,而波动拖累约等于方差的一半——成分越波动,能被拿回来的拖累越多:

成分年化波动均值   波动率目标 15.8%   买入持有 31.1%
等权组合年化波动   波动率目标 10.8%   买入持有 21.3%
B 相对 A 的增益    波动率目标 +0.70pp  买入持有 +2.87pp

买入持有的成分本来就更波动(它没有任何降波动作),所以在 B 口径下获益更多,直接把 +0.58 吃成了 -1.59。

"波动率目标跑赢买入持有"这句话,必须带上口径才成立。本节后续的状态拆分统一用 B 口径(等权组合日收益),因为按状态切分本来就要在同一条曲线上切。

复核脚本见 p5.6 的 code/agg_check.py;该节还会对这个 +0.58 pp 做区间检验,结论是区间跨 0

二、但"全样本年化"这个数几乎没有信息量

因为它把 16 年里完全不同的市场状态摊平成了一个数——这正是 p4.8 那个"平均血压"的问题。

拆开看。用两套完全不同的标签拆两次。

甲、事后划分(用当期前后各 63 日的区间收益)

策略族震荡
趋势跟随(双均线)19.033.71-19.22
均值回归(布林带)5.890.32-2.83
时序动量(12-1)25.503.48-18.71
波动率目标(15%)21.534.76-13.95
买入持有34.299.76-25.91

(单位:年化收益 %,8 只股票等权)

这张表符合所有人的直觉,而且能读出每个族的真面目:

  • 趋势跟随 = 削峰填谷:牛市比买入持有少赚 15.3 个百分点,熊市少亏 6.7 个百分点。它没有创造收益,它在搬运收益
  • 均值回归在熊市跌得最少(-2.83%),但牛市也只有 5.89%——它大部分时间空仓
  • 时序动量牛市最强(25.50%),因为它抓的就是持续性

乙、可实盘划分(只用过去 126 日收益,不含当日)

策略族近6月强近6月平近6月弱
趋势跟随(双均线)-2.463.717.08
均值回归(布林带)-0.75-1.2412.79
时序动量(12-1)-0.731.8911.88
波动率目标(15%)2.452.6515.96
买入持有-0.883.9431.82

每一族的最强状态都变成了"近6月弱"。

⚠️ 避坑

"这两张表矛盾"——它们不矛盾,它们在说不同的事。

甲表的标签是“这段时间是不是牛市”,乙表的标签是“过去半年涨了还是跌了”。

甲表需要知道未来 63 天,交易时你拿不到。乙表只用过去数据,交易时你拿得到——代价是它测的东西变了:它测的是"过去半年跌了 10% 之后,接下来的收益如何",而本样本给出的答案是 31.82% 年化

这就是为什么"牛市该做趋势、熊市该空仓"这类说法几乎无法证伪:

它成立的那张表,你交易时拿不到;你拿得到的那张表,结论不一样。

凡是按"牛市/熊市"给策略择时的说法,先问一句:那个标签,当时拿得到吗?

三、桶一样大,装的不是同一批日子

两套划分的桶大小几乎完全一致:

甲(事后)    1063    震荡 1978     856 
乙(可实盘) 近6月强 1063   近6月平 1977   近6月弱 856 

但重合度只有一半:

两套标签落在对应桶里的比例50.4%(3833 个可比较日)
事后"熊"的 856 日中,当时也标为"近6月弱"的309 日(36.1%)
事后"牛"的 1063 日中,当时标为"近6月强"的536 日(50.4%)

事后被认定为熊市的日子里,近三分之二在当时看起来并不弱。

这就是两张表结论相反的全部来源——不是策略变了,是"哪些日子被归到哪个桶"变了

四、波动率目标唯一跑赢,但先过成本这一关

它一只股票 16 年成交 2118 笔,平均两天一次。按 p4.2 的纪律,任何高换手结论都必须先做成本敏感性:

成本档波动率目标 年化%买入持有 年化%超额 pp
零成本3.743.00+0.73
默认(2.5bp + 5bp 滑点)3.583.00+0.58
翻倍(5bp + 10bp 滑点)3.452.99+0.46
四倍(10bp + 20bp 滑点)3.182.98+0.20

超额随成本单调衰减,四倍成本下只剩 0.20 个百分点——基本被抹平。

所以这个族的正确说法不是"波动率目标跑赢买入持有",而是:

在本样本、本成本假设下,它用几乎相同的收益换到了小得多的回撤(-41.4% vs -60.0%);而它的收益端超额对成本高度敏感,不该被当成 alpha 来报。

🔧 动手做:唯一"跑赢"的族,经不经得起成本?(5 分钟)

五个族里只有波动率目标族全样本跑赢买入持有——但它一年换手上百次。跑 python cost_check.py

你会看到(实测):它对买入持有的超额,零成本时 0.73pp,加上默认成本(2.5bp+5bp 滑点)只剩 0.58pp,而它做了 2118 笔成交。

想明白:唯一的"赢家"赢的幅度薄到 0.58pp、还靠上千笔换手撑着——成本再高一点、滑点估错一点,它就翻负。任何高换手的"跑赢",先过成本这一关,十有八九过不去。

五、族谱的正确用法

不是"从里面挑一个最好的",而是知道每个族的失效条件:

什么时候失效
趋势跟随无趋势的反复震荡(反复被打脸);牛市中放弃大量收益
均值回归趋势持续走坏时(下轨买入后继续跌)
时序动量趋势拐点处(信号最滞后)
波动率目标高换手 → 成本敏感;波动骤升时被动减仓,可能减在底部
买入持有需要承受 -60% 的回撤和 p4.8 那个 1207 日的水下期

而"什么时候该换族"这个问题,已经被本节堵死了一半:能给出漂亮答案的标签需要未来数据,能实时拿到的标签结论不同。

这正是 p5.5 滚动前推验证要正面处理的问题:不靠事后标签,而是只用截止当日的信息做决策,再看接下来发生了什么

❓ 测验
一份报告说:「我们的择时模型在熊市里跑赢基准 12 个百分点。」你最该先追问什么?
✏️ 填空
事后划分的「熊」共 856 日,其中当时也被标为「近6月弱」的只有 ___ 日,占 36.1%。

📌 免责:本课为技术教学,标的为沪深300 成分股按代码序取样(非精选),五个族均为教学演示口径,所有回测为历史模拟,不构成投资建议。

✅ 小结

三件事:

  1. 全样本年化几乎没有信息量。五个族里四个"策略"有三个跑输买入持有(3.00%);在逐股平均口径下唯一跑赢的波动率目标(3.58%)不预测任何方向,它的价值在最大回撤 -41.4% vs -60.0%,而收益端超额在四倍成本下只剩 0.20 pp——换成等权组合口径,这 +0.58 pp 直接翻成 -1.59 pp
  2. 同一批曲线,两套标签,结论相反。事后划分:趋势跟随牛市 19.03%、熊市 -19.22%(削峰填谷:牛市少赚 15.3 pp,熊市少亏 6.7 pp);可实盘划分:每一族的最强状态都是"近6月弱",买入持有 31.82%
  3. 原因是桶一样大但装的不是同一批日子:两套标签重合度 50.4%,事后"熊"的 856 日里当时也算弱的只有 309 日(36.1%)

一条可以直接用的追问:凡是按"牛市/熊市"给策略择时的说法,先问"那个标签当时拿得到吗"。

下一节进入多因子:因子怎么合成、为什么必须做行业与市值中性化、分层测试怎么读。只讲方法,不给任何标的名单。

下一节 → 多因子选股方法论:合成、中性化与分层
🔎 来源与核验· 5 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「五个族在 8 只股票上的全样本表现(2010-01 ~ 2026-07,均值):趋势跟随 0.43%/-59.4%/夏普 0.133、均值回归 0.30%/-43.0%/0.107、时序动量 1.17%/-61.6%/0.171、波动率目标 3.58%/-41.4%/0.301、买入持有 3.00%/-60.0%/0.256」
📚 本节 code/strategy_families.py,2026-07-31 于 ECS 实测,输出见 code/outputs/stdout.txt✓ 已核验 2026-07
「事后划分(前后各 63 日区间收益)下的年化:趋势跟随 牛 19.03/震荡 3.71/熊 -19.22;买入持有 34.29/9.76/-25.91;时序动量牛市 25.50 最高」
📚 同上实测✓ 已核验 2026-07
「可实盘划分(仅用过去 126 日收益)下的年化:五个族的最强状态均为「近6月弱」,买入持有 31.82%、波动率目标 15.96%、均值回归 12.79%」
📚 同上实测✓ 已核验 2026-07
「两套标签桶大小几乎相同(牛/近6月强 均 1063 日;熊/近6月弱 均 856 日)但重合度仅 50.4%;事后「熊」856 日中当时也标为「近6月弱」的仅 309 日(36.1%)」
📚 本节 code/regime_overlap.py,2026-07-31 于 ECS 实测,输出见 code/outputs/overlap_stdout.txt✓ 已核验 2026-07
「波动率目标族的成本敏感性(8 股均值):零成本超额 +0.73 pp、默认 +0.58、成本翻倍 +0.46、四倍 +0.20;平均成交 2097~2123 笔」
📚 本节 code/cost_check.py,2026-07-31 于 ECS 实测,输出见 code/outputs/cost_stdout.txt✓ 已核验 2026-07
事后划分含未来数据,仅用于收益归因,不可用于交易;两套划分的阈值均为 ±10%,窗口均为 126 个交易日。全样本表为「逐股年化再算术平均」口径,改用「日收益等权成组合」口径后波动率目标 4.28% vs 买入持有 5.87%(超额 -1.59 pp),复核见 p5.6 code/agg_check.py。
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · p5.3
多因子选股方法论:合成、中性化与分层
继续读下一节 →