# 第八章:让策略活着:监控、诊断、迭代 第七章你搭好了从回测到实盘的桥梁——订单生成、成交价压力测试、模拟交易、执行报告。策略终于跑起来了。 **然后你开始每天看净值。** 上个月赚了 3%,开心。这个月亏了 5%,慌了。盯着那条往下走的曲线,你脑子里全是问号——策略是不是坏了?要不要停?要不要改?还是再等等? 别慌。这一章教你三件事:怎么看(监控)、怎么查(诊断)、怎么改(迭代)。 **飞轮转回起点了。** 这一章你站在飞轮的“归因层”——但和第五章不同,第五章你“看”的是离线的回测;这一章你“看”的是活着的实盘。 本书口号“做要规则、看要指标、还要怀疑你的指标”——做(监控规则)、看(仪表盘指标)、疑(诊断三问),三个动作在这一章被你**第二次**走了一遍。 ### 路线图 **选什么标的(第二章)→ 每个买多少(第三章)→ 什么时候买卖(第四章)→ 怎么验证有效(第五章)→ 如何避免自欺欺人(第六章)→ 如何真正执行(第七章)→ 如何持续改进(第八章本章)** 本章从“盯着净值焦虑”的状态出发,用三个步骤建立系统性的迭代能力,三个步骤的问题与方法对照如表 8-1 所示。 **表 8-1 第八章三步迭代框架——问题与方法对照** | 步骤 | 问题 | 方法 | |------|------|------| | 第 1 步 | 策略是不是坏了? | 监控仪表盘——用指标替代焦虑 | | 第 2 步 | 问题出在哪? | 诊断三问——由外到内定位原因 | | 第 3 步 | 怎么改? | 假设驱动的对照实验 | (操作流程见前言“怎么使用这本书”。) --- ## 8.1 策略是不是坏了? 上个月赚了 3%,这个月亏了 5%。你盯着净值曲线,心里发慌。 但“净值在跌”这个信息太粗糙了——就像你身体不舒服,光说“不舒服”没用,得量体温、测血压、做检查。策略也一样,需要一个“仪表盘”,用具体的指标告诉你:现在到底是正常波动,还是真的出了问题。 我们用三个指标构成监控仪表盘,三者覆盖的角度如表 8-2 所示。 **表 8-2 监控仪表盘的三个核心指标** | 指标 | 含义 | 类比 | |------|------|------| | **滚动夏普**(最近 3 个月) | 最近 3 个月的风险调整收益 | 体温计——正常在 0 以上,跌破 0 说明在“发烧” | | **滚动回撤** | 从近期高点到现在跌了多少 | 水位线——持续下降说明“水在退” | | **对比沪深 300 多赚了多少**(你 - 沪深 300) | 同期你的策略和沪深 300 指数的差额 | 跑步比赛——持续落后说明跑法有问题 | ### 动手实验 1:监控仪表盘 这是本章第一份“运营 spec”,和前七章那种“建一个新功能”不同——这里我们要写的是**怎么周期性地看一个已经在跑的策略**。我们一起把这份 spec 写出来。这次重点看两件新东西:**心智切换**——上下文段第一句话就要告诉 AI“这不是建造,是体检”;以及**周期性运营 spec 的四要素**——触发条件 / 频率 / 阈值 / 输出形态全部显式。 #### 起草上下文 + 任务 上下文段开头那句“用具体指标替代焦虑”是整个第八章的总动机。运营章 spec 的上下文不必交代环境怎么搭,要交代**学员现在的情绪状态和场景**: > **上下文**:学员跑完第七章,策略终于在实盘运行。但上个月赚了 3%,这个月亏了 5%——是正常波动还是策略出了问题?需要一个“仪表盘”,用具体指标替代焦虑。 > > **任务**:在 notebook 中跑基准回测后,用 `StrategyMonitor` 构建监控仪表盘,画三个滚动指标的时间序列图,并自动检测恶化时段。 > 📌 **要点**:运营 spec 的上下文要交代“心智切换”。前七章的 spec 是“建一个新功能”,这一章开始是“看一个已有功能的健康状态”。学员如果带着建造者的姿态去做运维,看了 30 个图也不知道在干嘛——上下文一句话点明“用指标替代焦虑”,就是给学员(也给 AI)定调子:这次的产出不是新代码,是健康判断。 #### 起草要求:周期性运营 spec 的四要素 要求段是这份 spec 的主体——重点是把“什么时候触发监控、多久跑一次、什么数值算异常、异常了怎么输出”四件事**全部显式写明**: > **要求**: > > 1. 跑基准回测(`SimBroker` + 风险平价 + 21 天调仓 + 10% 止损),拿到 `result_base` > 2. 用 `StrategyMonitor(result_base, benchmark="510300.SS", roll_window=63)` 获取滚动指标 > - `roll_window=63` ≈ 3 个月交易日(21 天/月 × 3),太短(<20)每天报警、太长(>120)警报来时已经亏了半年 > 3. 画四行子图(figsize 14×16,共享 x 轴):净值曲线 / 滚动夏普(标零线和 1 线)/ 滚动回撤 / 相对沪深300超额收益,红色填充异常区域 > 4. 自动检测恶化时段——告警规则显式声明: > - **触发条件**:滚动夏普 < 0 且持续 ≥ 20 个交易日 > - **频率**:每月例行检查一次;第 1 步发现恶化时立即触发第 2 步诊断 > - **阈值**:`sharpe_threshold=0.0, min_days=20`(在 `StrategyMonitor` 构造时锁定) > - **输出形态**:遍历 `monitor.bad_periods`,每段一行打印起止日期、持续天数、平均夏普 > 5. 打印结尾过渡句:“2022 年集中出现恶化,原因是什么?”——把读者推向第 2 步 > 📌 **要点**:周期性运营 spec 的告警四要素——触发条件 / 频率 / 阈值 / 输出形态——必须全部显式。建造类 spec 的“成功标志”是“代码跑通、图渲染出来”;运营类 spec 的“成功标志”是“在该报警的时候报了警、不该报的时候没报”。**任何一个要素藏在 oxq 库默认参数里、没写进 spec,半年后阈值变了你也不知道**——这是运营 spec 区别于 q1-q7 建造 spec 的核心方法点。 > 📌 **要点**:关键常量旁边要写“为什么是这个数”。`roll_window=63` 不是随便选的——它是行业惯例(约 3 个月交易日),理由也要进 spec。运营场景里的每一个数字都是阈值,阈值的合理性比数值本身更重要。 #### 起草结果呈现 > **结果呈现**:四行仪表盘图正常渲染 + 恶化时段列表非空(2022 年应有多个时段)+ `equity`、`daily_ret`、`result_base`、`monitor` 变量已定义(后续 spec 接续使用)。 完整 spec 在 [`specs/spec-01-monitoring.md`](https://github.com/xingwudao/xquant-learning/blob/main/q8-iteration/specs/spec-01-monitoring.md)——复制给 AI,弹窗选「允许」。 AI 助手执行完毕后,你的 notebook 里应该出现了基准回测结果和一张四行的监控仪表盘图。 这个实验做了什么?先用 第四、七章 同款的风险平价策略(沪深300ETF + 纳指100ETF + 黄金ETF,21 天调仓 + 10% 止损)跑一次从 2021 年到现在的完整回测,拿到净值曲线。然后把净值喂给 `StrategyMonitor`——它就是那个“体检中心”,自动计算三个滚动健康指标(滚动夏普、滚动回撤、相对基准超额收益),并标注出指标持续恶化的时段。 ### 运行结果 先看基准回测:累计收益 86.45%,年化 12.50%,最大回撤 -16.62%,夏普 1.17,共 184 笔交易。 监控仪表盘的整体效果如图 8-1 所示。 ![监控仪表盘——四个指标同时恶化的时段就是“危险区”](images/q8-iteration/01-monitoring-dashboard.png) 图 8-1 第一行是净值曲线,整体向上但中间有几段明显的下跌。第二行是**滚动夏普**——红色填充区域就是夏普跌破 0 的时段,说明最近 3 个月策略在亏钱。第三行是回撤,持续加深说明策略在“失血”。第四行是相对沪深300的超额收益,为负说明还不如直接买指数。 仪表盘自动检测到的恶化时段如表 8-3 所示。 **表 8-3 监控仪表盘自动检测到的恶化时段** | 起始日期 | 结束日期 | 持续天数 | 平均夏普 | |----------|----------|----------|----------| | 2022-02-07 | 2022-04-29 | 58 天 | -1.78 | | 2022-05-05 | 2022-08-10 | 68 天 | -2.25 | | 2022-08-29 | 2022-09-30 | 23 天 | -1.13 | | 2022-10-10 | 2022-12-01 | 39 天 | -1.02 | | 2023-10-18 | 2023-11-14 | 20 天 | -0.84 | | 2023-12-05 | 2024-01-03 | 21 天 | -0.91 | 2022 年集中出现了 4 段恶化,最长的一段持续了 68 天,平均夏普 -2.25。 ### 结果解读 仪表盘让你从“盯着净值焦虑”变成“看指标判断”。 滚动夏普跌破 0,说明最近 3 个月策略在亏钱——这是最直接的“发烧”信号。回撤持续加深说明策略在“失血”。相对沪深300的超额收益为负,说明你还不如直接买指数。 三个指标同时恶化的时段就是需要关注的“危险区”。2022 年出现了多段集中恶化——但**发现问题不等于知道原因**。 是执行出了问题?市场变了?还是策略本身有缺陷? --- ## 8.2 问题出在哪? 第 1 步告诉你“最近不对劲”——但不对劲有很多种原因。 在开始诊断之前,先认识一个本节的关键概念: **市场状态(regime)** ——市场不是一成不变的。有时候波动大、几乎所有资产一起剧烈起伏(“高波动”),有时候很平静(“低波动 / 正常”)。就像天气有晴天和台风天,同一件衣服在不同天气穿感受完全不同。策略也一样——一个在正常市场表现不错的策略,到了高波动状态可能完全失灵。**诊断三步里有两步都和它相关**——所以我们前置介绍它。 就像身体不舒服,你不会直接吃药。先量体温排除感冒,再查血排除感染,最后做 CT 看看有没有更深的问题。策略诊断也一样,我们由外到内排查三层: 1. **是执行的问题吗?** —— 回测和实盘之间的差距有没有异常放大 2. **是市场状态(regime)变了吗?** —— 市场的波动水平有没有发生大的变化 3. **是策略本身的设计问题吗?** —— 参数在不同时段的表现是否稳定 ### 动手实验 2:诊断三问 我们一起把这份 spec 写出来。这次重点看两件新东西:**一份 spec 包多步诊断的取舍**——前七章每份 spec 只做一件事,这份一次塞进三步诊断;以及**阈值事前锁定**——什么样的数字算“通过”、什么样算“失败”,必须在跑诊断之前定好,不能跑完了再看图说话。 #### 起草上下文 + 任务 接续型 spec 链的上下文要写明前置 spec 留下了哪些变量、本份 spec 在叙事里的位置: > **上下文**:在 `q8-iteration.ipynb` 中已有第 1 步监控仪表盘和恶化时段检测。第 1 步发现了多段恶化时段(2022 年集中出现),现在需要搞清楚原因。由外到内排查三层:执行 → 市场 → 策略。 > > **任务**:完成三步诊断——排查执行落差、检测市场状态变化、检查参数稳定性。这三步是不可拆分的方法论,必须按“由外到内”的顺序执行。 > 📌 **要点**:q1-q7 的写法是“一份 spec 一件事”,这份是“一份 spec 三件事”——叙事完整性优先于任务原子性。诊断方法论本身就是一个不可拆分的整体,拆成三份 spec 学员会丢掉“先排外因再查内因”的节奏。但代价是 spec 体积变大,所以**必须在任务段第一句就明示“这三步必须按顺序”**——给 AI 也给学员定下“这是一个流程,不是三个独立任务”的预期。 #### 起草要求:阈值事前锁定 要求段是这份 spec 的主体——三步诊断每一步都要把“通过 / 失败”的判定阈值事前写明,绝不能“看图说话”: > **要求**: > > 1. **诊断 1——执行落差排查**: > - 跑“理想回测”(收盘价 + 佣金,无滑点)和“模拟实盘”(次日开盘 + 佣金 + 千分之一滑点) > - 计算两条净值曲线的滚动执行落差(63 日滚动均值) > - **判定阈值**:落差走势平稳(每 63 日变化标准差 < 0.5%)→ 排除执行问题;若突然放大则结论改为“需进一步检查滑点 / 佣金模型” > - 把落差放到学员能理解的语境中:N 年赚了 X%,其中 Y% 被执行成本吃掉 > > 2. **诊断 2——市场状态检测**: > - 用 `MarketStateDetector(result_base)` 检测市场状态 > - 分状态打印策略表现(年化收益、夏普、天数) > - **判定阈值**:高波动状态下夏普 < 0 且与第 1 步恶化时段重叠 ≥ 70% → 结论“市场状态变了,不是策略 bug” > - 画两行子图:净值曲线 + 高波动红色阴影 / 市场波动率 + 中位数线 + 高波动阈值线 > - `detector`、`high_vol_mask`、`market_vol`、`HIGH_VOL_LINE` 变量必须已定义(第 3 步接续使用) > > 3. **诊断 3——参数稳定性检查**: > - 把数据分成前后两半(按交易日中点切分) > - 对 5 种调仓频率(10/15/21/42/63 天)分别在两半上跑回测 > - **判定阈值**:用 `scipy.stats.kendalltau` 计算前后两半夏普排序的 tau(Kendall tau 是衡量两组排序一致性的统计量,取值 -1 到 1,越接近 1 说明前后两段的最优参数排序越像);tau ≥ 0.7 → 排序稳定,参数没过拟合;tau < 0.7 → 参数对时段敏感 > - 打印对比表:频率、前半段年化、前半段夏普、后半段年化、后半段夏普 > 📌 **要点**:诊断 spec 的核心是“事前锁定阈值”——绝不能“看图说话”。建造类 spec 的失败长什么样很明显(代码报错、图渲染不出来);诊断类 spec 的失败是隐式的——你跑完看到一个图,凭直觉说“看起来没问题”。**只要阈值不事前锁定,诊断就退化成 confirmation bias**——你想看到什么结论就能从图里读出什么结论。每一步诊断都必须配一个“通过 X、失败 Y”的数字阈值。 > 📌 **要点**:spec 不能给 AI“二选一”。如果你写“用 Kendall tau 或简单比较都行”,AI 选哪种你就拿到哪种结果,下次重跑可能换一种实现,结果不可比。**spec 是契约不是协商**——能确定一种方案就锁死一种,留 AI 自由发挥的余地越小,诊断越可复现。 #### 起草结果呈现 > **结果呈现**:三步诊断各一组图 + 数字结论;`detector`、`high_vol_mask`、`market_vol` 变量已定义供第 3 步使用。 完整 spec 在 [`specs/spec-02-diagnostics.md`](https://github.com/xingwudao/xquant-learning/blob/main/q8-iteration/specs/spec-02-diagnostics.md)——复制给 AI,弹窗选「允许」。 AI 助手执行完毕后,你的 notebook 里应该出现了三步诊断的完整结果。 这个实验做了什么?由外到内排查三层原因: - **诊断 1(执行)**:跑两次回测——一次用“理想条件”(收盘价成交、只有佣金),一次用“模拟实盘”(次日开盘价 + 佣金 + 滑点),对比两条净值曲线的差距是否在正常范围内。 - **诊断 2(市场)**:用 `MarketStateDetector` 自动检测市场的波动率水平,把每一天标记为“高波动”或“正常”,然后分别统计策略在两种状态下的表现。 - **诊断 3(参数)**:把整段回测数据按时间中点切成前后两半(前半段大致覆盖 2021-2023 上半年的高波动市场,后半段覆盖 2023 下半年至今的恢复期),在两半上分别测试 5 种调仓频率,看最优参数在不同时段是否稳定。 ### 运行结果 按“由外到内”的顺序看三步诊断的输出。 #### 诊断 1 — 执行落差排查 理想回测收益 92.13%,模拟实盘收益 83.54%,执行落差累计 8.59%,每年约 1.72%。 换个方式理解:策略 5 年累计赚了 92%,其中 8.6% 被佣金、滑点等执行成本吃掉了。相当于每年的执行成本约 1.7%——对于月度调仓的策略来说,这是正常水平。 更重要的是执行落差的走势是平稳的,没有突然放大——说明执行层面没有异常。如果这条线突然变陡(落差加速扩大),才说明执行出了问题。 排除执行问题。 #### 诊断 2 — 市场状态检测 回顾 8.2 节首讲过的**市场状态(regime)**——这一步我们用 `MarketStateDetector` 把每一天自动标记为高波动 / 正常 / 低波动,再看策略在不同状态下的表现差异。结果三档分得很开——表 8-4 一目了然。 策略在不同市场状态下的表现差异如表 8-4 所示。 **表 8-4 策略在不同市场状态下的表现** | 市场状态 | 年化收益 | 夏普 | 天数 | |----------|----------|------|------| | 高波动 | -1.44% | -0.08 | 260 天 | | 正常 | 16.62% | 1.80 | 940 天 | | 低波动 | 34.72% | 6.32 | 36 天 | 策略净值与高波动时段的叠加如图 8-2 所示。 ![策略净值 + 高波动时段——恶化和高波动高度吻合](images/q8-iteration/02-market-state.png) 图 8-2 的红色阴影区域就是高波动时段。和第 1 步检测到的恶化时段一对比,高度吻合——策略在高波动环境下年化收益 -1.44%,夏普 -0.08,在亏钱。而正常时段年化 16.62%,夏普 1.80,表现优秀。 恶化的原因是市场状态变了,不是策略的 bug。 #### 诊断 3 — 参数稳定性检查 把数据按时间中点切成前后两半,对 5 种调仓频率分别跑回测,结果如表 8-5 所示。 **表 8-5 5 种调仓频率在前后两半段的表现** | 频率 | 前半段年化 | 前半段夏普 | 后半段年化 | 后半段夏普 | |------|-----------|-----------|-----------|-----------| | 10 天 | +3.30% | 0.40 | +25.78% | 2.17 | | 15 天 | +1.91% | 0.25 | +23.76% | 2.02 | | 21 天 | +2.18% | 0.28 | +23.13% | 1.89 | | 42 天 | +2.88% | 0.37 | +24.82% | 1.95 | | 63 天 | +5.34% | 0.65 | +24.56% | 1.87 | 不同时段的年化收益差异很大(前半段 2-5%,后半段 23-26%),说明策略在不同市场环境下表现差异大。但参数排序有变化——前半段 63 天最优,后半段 10 天最优。这意味着:策略的框架是对的,但对某些市场状态没有应对方案。 ### 结果解读 三步诊断的结论汇总如表 8-6 所示。 **表 8-6 三步诊断的结论汇总** | 诊断 | 问题 | 结论 | |------|------|------| | 执行 | 回测和实盘的差距有没有异常放大? | 没有。执行落差稳定,排除 | | 市场 | 市场波动水平有没有大的变化? | 有。恶化时段和高波动高度吻合 | | 策略 | 参数在不同时段是否稳定? | 排序有变化,但策略框架没问题,对某些市场状态没有应对方案 | 问题定位清楚了——不是执行的问题,不是参数过拟合,而是**策略对高波动的市场状态没有应对方案**。 把这套诊断流程压成一句以后能脱口而出的口诀—— > **🔧 归因 SOP 口诀:先排执行落差(外),再查市场状态(中),最后看策略本身(内)。** 这个由外到内的顺序不能反过来。先动策略本身,你大概率会“为了一个市场状态变化”而推翻整个框架,下一轮市场恢复正常你又要再推翻一次——这正是缺乏纪律的迭代最常见的死循环。三步走完之后再决定改什么、改哪一层,每一步都有数据支撑。 那怎么改?推翻重来?还是在现有策略上做小步改进? --- ## 8.3 怎么改? 诊断结果出来了——执行没问题,是市场状态变了,策略对高波动没有应对。 你可能想直接调参数试试。但改策略不是拍脑袋——你需要像做实验一样:**先提出假设和判断标准,再做对照实验,最后看结果确认还是推翻假设。** 就像医生看病——不是看到头疼就开止疼药,而是先做检查确认病因,再开药。 每次迭代都走这个闭环: ``` 观察(看到了什么)—— 假设(猜原因)—— 验证标准(怎么判断对错)—— 实验(改一个变量)—— 结论(确认/推翻) ``` ### 动手实验 3:假设驱动的对照实验 我们一起把这份 spec 写出来。这次重点看两件新东西:**让“假设”跟着 Strategy 跑**——open-xquant 的 `Strategy` 类在设计时就内置了 `hypothesis` 和 `objectives` 两个空,创建 Strategy 时填两个空(假设、判定标准),跑出来的 Strategy 自己背着这张假设卡;以及**验证标准事前锁定具体数字**——绝不写“看情况调整”这种橡皮条款。 #### 起草上下文 + 任务 > **上下文**:在 `q8-iteration.ipynb` 中已有第 1 步监控 + 第 2 步诊断的完整变量。诊断结论:策略对高波动的市场状态没有应对方案。 > > **任务**:完成两轮迭代实验(调仓频率 + 波动率过滤),用 Strategy 的 `hypothesis`/`objectives` 字段展示完整的实验工作流,最后用 `ExperimentLog` 记录迭代历史。 #### 起草要求:假设跟着 strategy 跑 + 验证标准事前锁定 要求段是这份 spec 的灵魂——把“假设 → 验证标准 → 实验 → 结论”的方法论挂到 Strategy 自己身上的两个空(`Strategy.hypothesis` 和 `Strategy.objectives`),让 strategy 跑到哪、假设卡跟到哪: > **要求**: > > 1. **迭代 1——调仓频率对照实验**(注定被推翻的一轮): > - 创建 Strategy 时填写 `hypothesis="缩短调仓频率能改善高波动期表现"` > - 同时填写 `objectives={"high_vol_sharpe": "above_baseline", "max_dd_limit": dd_limit}`——验证标准事前锁定 > - 对 6 种频率(5/10/15/21/42/63 天)分别跑回测 > - 计算每种频率在高波动时段的夏普(用 `high_vol_mask` 筛选) > - 假设验证:对比基准(21 天)和最优频率的高波动夏普,判断假设确认还是推翻 > > 2. **迭代 2——波动率过滤对照实验**(注定被确认的一轮): > - 创建 Strategy 时填写 `hypothesis="高波动时降仓位能显著降低回撤"` > - 验证标准事前锁定:`objectives={"dd_improvement": ">=30%", "sharpe": ">=1.0"}` > - 用 `VolFilteredOptimizer` 包装 `RiskParityOptimizer`:高波动期权重缩放 0.5 > - 测试 4 种配置:无过滤、阈值 10% / 15% / 20% > - **“最佳配置”选择函数显式锁定**:`best_label = argmax(dd_improvement)`——不能让 AI 揣摩“最佳”的含义 > - 假设验证:检查最佳配置是否同时满足两个事前标准 > > 3. **迭代记录表**: > - 用 `ExperimentLog(name="第八章迭代实验")` 创建记录 > - 用 `log.add()` 添加两轮迭代,每行字段值字符级显式:`name` / `observation` / `hypothesis` / `criteria` / `result` / `conclusion` / `notes` > - 用 `log.to_dataframe().to_string(index=False)` 打印记录表 > 📌 **要点**:让 hypothesis 跟着 strategy 跑——`hypothesis` 和 `objectives` 这两个空是 open-xquant 在 `Strategy` 类设计时就内置好的。spec 要求“创建 Strategy 时填写”这两个空,意思是把“假设驱动”从写在注释里、口头讲讲的方法论,挂到 Strategy 自己身上——跑出来的 Strategy 对象自带 `.hypothesis` 标签,`ExperimentLog` 自动取走填进表格。**以前你的假设写在心里、写在备忘录里——现在 Strategy 自己背着这张假设卡。** > 📌 **要点**:验证标准必须事前锁定到具体数字。“高波动夏普优于基准”是数字、“回撤改善 ≥ 30% 且夏普 ≥ 1.0”是数字——绝不写“看情况调整”“效果好就采纳”这种橡皮条款。**事后改标准就是过拟合**——你跑完看到 25% 改善,临时把标准从 30% 改到 20%,那不是验证假设,是在为已有结果找说辞。事前锁定阈值是迭代 spec 区别于“调参 spec”的本质差异。 > 📌 **要点**:注意章节的节奏——迭代 1 故意安排成“假设被推翻”,迭代 2 是“假设被确认”。这是教学设计:先让学员体会“推翻不是失败、推翻是省下了实盘的学费”,再示范一个被确认的假设长什么样。spec 也要配合这个节奏——迭代 1 不要写得“温柔一点让它更容易通过”,要让结果如实推翻。 #### 起草结果呈现 > **结果呈现**:两轮指标表 + 净值对比图 + 假设验证结论 + 2 行迭代记录表(iter1 rejected + iter2 confirmed)+ Strategy 的 hypothesis / objectives 字段值在输出中可见。 完整 spec 在 [`specs/spec-03-iteration.md`](https://github.com/xingwudao/xquant-learning/blob/main/q8-iteration/specs/spec-03-iteration.md)——复制给 AI,弹窗选「允许」。 AI 助手执行完毕后,你的 notebook 里应该出现了两轮对照实验的完整结果和迭代记录表。 这个实验做了什么?两轮对照实验,每轮都遵循“假设 —— 验证标准 —— 实验 —— 结论”的流程: - **迭代 1(调仓频率)**:假设“缩短调仓频率能改善高波动期表现”。保持策略逻辑不变,只改调仓周期——分别测试 5/10/15/21/42/63 天六种频率,重点比较各频率在高波动时段(第 2 步检测出的那些红色阴影区域)的夏普。 - **迭代 2(波动率过滤)**:假设“高波动时降低仓位能显著减少回撤”。保持调仓频率不变,用 `VolFilteredOptimizer` 包装 `RiskParityOptimizer`——当市场波动率超过阈值时,自动将权重缩放 0.5(仓位减半)。分别测试无过滤、阈值 10%/15%/20% 四种配置,看回撤改善了多少、付出了多少收益代价。 open-xquant 的 `Strategy` 支持在创建时填写 `hypothesis`(假设)和 `objectives`(验证目标),让“假设驱动”从口号变成 Strategy 自己背着的两个标签。`ExperimentLog` 则帮你自动记录每次迭代的假设、标准、结果和结论。 ### 运行结果 两轮迭代的产出按“先被推翻、后被确认”的节奏展开。 #### 迭代 1 — 调仓频率 观察:高波动时段策略表现最差。假设:21 天调仓太慢,缩短频率能改善。验证标准:高波动时段夏普优于基准。 6 种调仓频率的全时段与高波动指标对比如表 8-7 所示。 **表 8-7 迭代 1——6 种调仓频率的指标对比** | 频率 | 年化收益 | 最大回撤 | 夏普比率 | 高波动夏普 | |------|----------|----------|----------|------------| | 5 天 | +13.01% | -14.47% | 1.25 | 0.22 | | 10 天 | +14.55% | -12.57% | 1.39 | 0.56 | | 15 天 | +13.29% | -14.14% | 1.26 | 0.09 | | 21 天(基准) | +12.50% | -16.62% | 1.17 | -0.08 | | 42 天 | +14.09% | -15.98% | 1.29 | 0.44 | | 63 天 | +14.71% | -11.49% | 1.31 | 0.84 | 不同调仓频率的净值曲线对比如图 8-3 所示。 ![迭代 1:不同调仓频率的净值对比——图例中是高波动时段的夏普](images/q8-iteration/03-iter1-rebal-freq.png) 注意看表 8-7 最后一列——“高波动夏普”,这才是我们验证假设的指标。全时段夏普最高的是 10 天(1.39),但那不是我们的验证目标。高波动时段表现最好的是 63 天(0.84)——**更长而非更短**。 **假设被推翻。** 缩短调仓频率并没有改善高波动期表现。 这告诉我们什么?改“多久做一次决策”没有用——因为每次做的决策(风险平价满仓分配)没变。问题不在于你多久看一次天气预报,而在于看到暴雨预警后有没有带伞。 #### 迭代 2 — 波动率过滤 观察:频率不是关键,真正的问题是高波动时策略依然满仓暴露在风险中。假设:高波动时降仓位,能显著降低回撤。验证标准:回撤改善 ≥ 30%,夏普 ≥ 1.0。 4 种波动率过滤配置的指标对比如表 8-8 所示。 **表 8-8 迭代 2——4 种波动率过滤配置的指标对比** | 配置 | 年化收益 | 最大回撤 | 夏普比率 | 回撤改善 | |------|----------|----------|----------|----------| | 无过滤(基准) | +12.50% | -16.62% | 1.17 | — | | 过滤>10% | +6.57% | -8.70% | 1.18 | 47.6% | | 过滤>15% | +10.44% | -9.62% | 1.43 | 42.1% | | 过滤>20% | +12.89% | -12.43% | 1.28 | 25.2% | 不同配置的净值曲线对比如图 8-4 所示。 ![迭代 2:波动率过滤的净值对比——高波动时仓位减半](images/q8-iteration/04-iter2-vol-filter.png) 最佳配置是“过滤>10%”:回撤从 -16.62% 改善到 -8.70%(改善 47.6%),夏普 1.18。两个验证标准都达标。 **假设被确认。** 波动率过滤有效。 但年化收益从 +12.50% 降到 +6.57%,代价 5.93%。你用 5.9% 的收益换来了 48% 的回撤改善——这就是**取舍(trade-off)**。没有“最好”的参数,只有“适合你风险承受能力”的选择。 ### 结果解读 两轮迭代的关键认知汇总如表 8-9 所示。 **表 8-9 两轮迭代的核心认知** | | 改了什么 | 学到了什么 | |---|---|---| | 迭代 1 | 执行参数(多久做一次) | 问题不在频率——策略对市场状态没有反应 | | 迭代 2 | 策略逻辑(做什么) | 让策略感知市场状态并调整行为才有效 | 假设被推翻不是失败——这是最有价值的学习。如果你凭直觉觉得“调快点应该更好”,然后不做验证就上线,可能越改越差。**对照实验的意义就在于此:用数据而不是直觉做决策。** **“执行参数”和“策略逻辑”是两件事。** 调仓频率是“多久行动一次”,但每次行动的内容(满仓风险平价)没变。如果问题出在行动的内容上,改频率没用——你需要改策略的行为本身。 --- ## 8.4 回头看:你刚才做了什么? 三步实验,回答了同一个核心问题——**策略跑起来之后怎么办?** 第 1 步:建监控,发现“最近不对劲”(滚动夏普跌破 0)。第 2 步:做诊断,定位“是市场状态变了”(由外到内三层排查)。第 3 步:做迭代,改频率没用(推翻),改逻辑有效(确认)。 从今以后,策略出了问题你都可以套这套 SOP 应对,三步对照清单如表 8-10 所示。 **表 8-10 监控/诊断/迭代 SOP 速查** | 步骤 | 怎么做 | 好的信号 | 坏的信号 | |------|--------|----------|----------| | 监控 | 看滚动夏普、回撤、超额 | 指标波动但整体健康 | 多个指标同时持续恶化 | | 诊断 | 由外到内:执行、市场、策略 | 能定位到具体原因 | 三层都有问题(可能需要重新设计) | | 迭代 | 假设、验证标准、实验、结论 | 假设被确认或被有价值地推翻 | 没有假设就开始改参数 | --- ## 8.5 本章总结 ### 策略进化路径 第七章把策略推上了实盘,第八章把“上线之后怎么办”补完——三步进化路径如表 8-11 所示。 **表 8-11 第八章三步进化路径** | 步骤 | 做了什么 | 关键发现 | |------|---------|---------| | 第 1 步 监控 | 滚动指标 + 自动标恶化 | 2022 年集中出现 4 段恶化 | | 第 2 步 诊断 | 由外到内三层排查 | 不是 bug,是市场状态变了 | | 第 3 步 迭代 | 两轮假设驱动对照 | 改频率没用,改逻辑才有效 | 第七章产出“从回测到实盘的完整闭环”,第八章在它之上多走一步——产出“策略迭代的完整方法论”加上“用数据做决策”的认知。 ### 概念速查表 本章涉及的核心概念汇总如表 8-12 所示,方便随时回查。 **表 8-12 第八章核心概念速查** | 概念 | 含义 | 类比 | |------|------|------| | 滚动夏普 | 最近一段时间(如 3 个月)的风险调整收益,衡量策略“现在”的健康度 | 体温计——数值正常说明没发烧 | | 市场状态(regime) | 市场的波动水平和整体环境,不同时期差异很大 | 天气——晴天和台风天出门穿的衣服不一样 | | 假设驱动迭代 | 先提出可验证的猜想和判断标准,再做实验,而不是凭感觉改 | 医生看病——先检查确认病因,再开药 | | 对照实验 | 只改一个变量、其他不变,看效果 | 同一道菜只改盐的量,尝尝区别 | | 执行参数 vs 策略逻辑 | “多久做一次”和“做什么”是两件事 | 多久看一次天气预报 vs 看到暴雨预警后是否带伞 | | 迭代记录表 | 每次改动的假设、标准、结果、结论,形成可追溯的改进历史 | 病历本——记录每次看病的诊断和治疗 | ### 飞轮第一圈完整闭环 **到这里,你完成了第一圈完整的飞轮——本章的结论会反过来喂给下一圈飞轮的“候选 → 组合 → 执行”。** 候选层(第二章)→ 组合层(第三、四章)→ 执行层(第七章)→ 归因层(第五、六、八章)→ 反哺起点。三字要点(做 / 看 / 疑)也走了完整一遍:做 = 把每一层都写成可重复执行的规则;看 = 用指标判断好坏;疑 = 不止信回测,也质疑实盘的每一段。 下一圈飞轮要让它转得更快——这就是第九章的事。 ### 本章核心认知 走完三步之后,本章最值得带走的九条认知如表 8-13 所示。 **表 8-13 第八章核心认知** | 认知 | 来源 | |------|------| | 策略亏钱不一定是坏了,可能是正常波动 | 第 1 步 | | 持续监控比偶尔看一眼净值重要得多 | 第 1 步 | | 诊断要由外到内——先排执行,再看市场,最后看策略 | 第 2 步 | | 市场状态会变,策略在不同状态下表现不同是正常的 | 第 2 步 | | 改策略前先定好验证标准,不能事后改规则 | 第 3 步 | | 改“多久行动”和改“怎么行动”是两件事 | 第 3 步 迭代 1 | | 假设被推翻是好事——省下了实盘亏钱的学费 | 第 3 步 迭代 1 | | 让策略感知市场状态并调整行为,才是有效的改进 | 第 3 步 迭代 2 | | 每次改进都是取舍——用收益换安全,或反过来 | 第 3 步 迭代 2 | ### 带走的问题 我们已经走完了从选标的(第二章)到持续改进(第八章)的完整闭环。你现在掌握了监控、诊断和迭代的方法论。 但你可能已经感受到:每次迭代都要构造假设、改参数、跑回测、对比指标、记录结论……这个过程不算轻松。 策略不完美没关系,碰到问题也没关系——只要迭代得够快,从错误和失败中学习得够快,你就可以保持进化。**迭代速度本身就是竞争力。** 有没有办法让这个循环转得更快?—— 第九章 > 本章所有代码的可运行版本见 `notebooks/q8-iteration.ipynb` --- ## 8.6 拓展阅读 本章正文聚焦“监控 / 诊断 / 迭代”三个策略层面的动作。下面这段是动手之外的“软功”——读完正文后回头看,决定你能不能把这套方法长期跑下去。 ### 迭代自己:耐心 / 纪律 / 记录 前面三步解决的是策略层面的问题,但还有一层问题藏在你自己身上。 **耐心(对应第 1 步 监控)。** 策略有波动是常态。不要每天盯着净值焦虑——你已经有了监控仪表盘,定期检查就够了。如果你发现自己每小时都在看净值,那不是在监控,是在焦虑。仪表盘的意义不只是发现问题,也是让你在没有问题的时候安心。 **纪律(对应第 3 步 验证标准)。** 定好了验证标准就不要改。如果实验结果不好就改标准,这和第六章讲的过拟合是一回事——你在“事后”调整规则来让结果看起来好,而不是真的在改进。假设被推翻了?接受它,记录下来,想下一个假设。 **记录(对应第 3 步 迭代记录表)。** 每次迭代都填迭代记录表。三个月后回头看,你会发现大部分假设都被推翻了——但你从每次推翻中学到的东西比确认更多。这些记录就是你的成长轨迹。 **一句话:** 策略层面的方法论你已经有了;接下来三年里能不能跑通,取决于你“耐心 / 纪律 / 记录”这三条软纪律守得有多严。这是带得走的“心态地图”。 --- ## 反馈与读者群 如果你发现本章有错别字、概念不清或实验结果不一致,欢迎 [提交 GitHub Issue](https://github.com/xingwudao/xquant-beginner/issues/new/choose)。 课程、正式书和后续更新见 [xquant.shop/courses](https://xquant.shop/courses)。 添加作者微信 `xquanter` 进入读者群。请备注:`XQuant 读者`。