# 第四章：给策略加规则：调仓、止损、止盈

第三章我们用三种信号解决了“买多少”——等权、风险平价、动量排名，各有各的性格。但有个细节你可能没注意：不管用哪种信号，策略都是每 10 个交易日自动调仓一次——重新把持仓比例调回目标权重。为什么是 10 天？5 天行不行？一个月呢？

更关键的问题是：**如果在两次调仓之间，某只 ETF 突然暴跌 20%，你只能干等到下一个调仓日才能反应。** 这合理吗？

> **🧭 本章边界**：章名虽然叫“什么时候买卖”，但本章**不做入场择时**——什么时候第一次进场、用什么信号判断“该买了”，那些是第三章信号已经回答的事。本章聚焦的是**已经持仓之后**的三个执行节奏：多久做一次再平衡（4.1）、跌到什么程度先撤（4.2）、涨到什么程度走人（4.3）。
>
> **🧱 一个新元概念：规则（Rule）。** 第三章信号说的是“想怎么分钱”——是策略的大脑。本章引入的是**规则**——策略的手脚：感知持仓状态（涨了多少、跌了多少、上次调仓多久了），按预设条件触发动作。信号管“想怎么动”，规则管“什么时候动手”。两者分工明确，缺一不可。从本章起，你会反复看到 `RebalanceFrequencyRule` / `StopLossRule` / `TakeProfitRule` 这些 Rule 类——它们就是规则的具体实现。

### 路线图

我们正在走一条完整的策略构建之路：

**选什么标的（第二章 已完成）→ 每个买多少（第三章 已完成）→ 什么时候买卖（第四章 本章）→ 怎么验证有效（第五章）**

**这一章你仍在飞轮的“组合层”，主练“做”——把“什么时候动手”也写成可重复执行的规则。** 本章解决“什么时候买卖”，分三步揭示三个认知，最后你会发现：不是所有“听起来合理”的规则都有效。三节的问题与方法对照如表 4-1 所示。

**表 4-1 第四章三节问题与方法对照**

| 节 | 问题 | 方法 |
|----|------|------|
| 4.1 | 调仓频率怎么选？ | 周/月/季度对比 + 引入交易成本 |
| 4.2 | 亏了要不要先跑？ | 止损规则 |
| 4.3 | 赚了要不要走？ | 止盈规则 |

本章使用第三章的风险平价（RiskParity）作为基础信号——它始终持有全部 3 只 ETF，方便我们观察不同规则的效果。

（操作流程见前言“怎么使用这本书”。每一节按相同流程：阅读 → 复制 spec 给 AI → 看结果与解读。）

---

## 4.1 调仓频率有多重要？

第三章每 10 个交易日调一次仓——这个“10”是拍脑袋定的。调得更勤（每周）会更好吗？调得更懒（每季度）会更差吗？

直觉告诉我们：调得越勤，反应越快，效果应该越好。真的吗？

### 动手实验 1：不同调仓频率对比

本节用到两个新模块：`RebalanceFrequencyRule`（设定多少天调仓一次）和 `PercentageFee`（模拟交易手续费）。

我们一起把这份 spec 写出来。这次重点看两件新东西：**参数化扫描怎么列**，以及**辅助函数封装作为后续 spec 的接口**。

#### 起草上下文 + 任务

> **上下文**：本章探讨“什么时候买卖”。第三章用了三种信号（等权、风险平价、动量排名）。本章选择风险平价（RiskParity）作为基础信号——它始终持有全部 3 只 ETF，方便我们观察止损和止盈规则的效果。第三章每 10 个交易日调一次仓，但为什么是 10 天？调得更勤或更懒，结果会怎样？
>
> **任务**：在 notebook `q4-when-to-trade.ipynb` 中创建代码，对比不同调仓频率的效果，并首次引入交易成本。

#### 起草要求：参数化扫描 + 辅助函数封装

要求段是这份 spec 的灵魂——把“对比 4 种频率”和“先后跑两轮成本”两件事拧成一份 spec。

> **要求**：
>
> 1. 先读 oxq 源码：`PercentageFee` / `SimBroker` / `RebalanceFrequencyRule` / `RiskParityOptimizer` / `Engine` / `Strategy`
> 2. 复用第三章的 SYMBOLS、START、RiskParity 信号等基础配置
> 3. 定义辅助函数 `run_backtest(frequency, fee_model=None, order_rules=None)` 封装策略构建和回测执行，**供后续 spec 复用**
> 4. 定义 4 种调仓频率：`FREQUENCIES = [5, 10, 21, 63]`（约每周 / 两周 / 每月 / 每季度）
> 5. 第一轮回测**不含成本**（`fee_model=None`）→ 第二轮回测**含成本**（`PercentageFee(rate=Decimal("0.001"), min_fee=Decimal("5"))`）
> 6. 含成本版本画净值曲线对比图（figsize 12x6，4 条线，归一化到起点 = 100）
> 7. 分析按实际数据动态描述（不要硬写“更高”或“更低”）

> 📌 **要点**：参数化扫描的关键是把“被对比的维度”列成一个数组（`FREQUENCIES = [5, 10, 21, 63]`）+ 一个 for 循环跑完。spec 里把数组写出来，AI 就不用猜要对比哪些值。后面 spec-02 / spec-03 也会沿用同样的写法——`STOP_THRESHOLDS = [...]`、`TP_THRESHOLDS = [...]`。这是研究型工作中最常见的 spec 套路。

> 📌 **要点**：辅助函数 `run_backtest` 是为 spec-02 / spec-03 留的接口。整章三份 spec 都通过它跑回测，spec-02 把止损规则塞进 `order_rules` 参数，spec-03 再把止盈塞进去。**spec 链有共享逻辑时，把它封装成函数放在源头 spec，后续 spec 直接调用——这是接续型 spec 链的核心架构。**

#### 起草结果呈现

> **结果呈现**：
>
> 1. 无成本对比表（4 行 6 列）
> 2. 含成本对比表（4 行 7 列，多一列“总手续费”）
> 3. 含成本净值曲线对比图
> 4. 分析文字（按实际数据描述方向）

完整 spec 在 [`specs/spec-01-rebalance-frequency.md`](https://github.com/xingwudao/xquant-learning/blob/main/q4-when-to-trade/specs/spec-01-rebalance-frequency.md)——复制给 AI，弹窗选「允许」。

AI 助手执行完毕后，你的 notebook 里应该出现了两张对比表和一张净值图。我们来解读结果。

四种频率分别对应每周（5 个交易日）、每两周（10）、每月（21）、每季度（63）。代码跑了两轮回测：第一轮不含成本，第二轮含成本（万分之一佣金，最低 5 元）。同样的策略，唯一的差别是有没有扣手续费。

**交易成本（Transaction Cost）** ——每次买卖都要付的费用，主要是付给券商的**佣金**和（部分市场）付给政府的**印花税**。本章回测里只模拟了佣金部分（万分之一，最低 5 元），没有加印花税——实际交易的成本会比回测里更高。记住这一点：**回测里的成本是乐观估计，现实只会更贵。** A 股 / 美股 / 港股具体费率有差别，详情见章末「拓展阅读」。

在之前的第二章和第三章里，我们的回测都没有考虑交易成本，就像在一个“免手续费”的理想世界里做实验。从现在开始，我们把手续费加回来，看看现实世界会怎样。

### 实验结果

不含成本时四种频率的对比如表 4-2 所示。

**表 4-2 不含交易成本时四种调仓频率的对比**

| 频率 | 累计收益率 | 年化波动率 | 最大回撤 | 夏普比率 | 交易次数 |
|------|-----------|-----------|---------|---------|---------|
| 5 | 101.22% | 10.90% | -12.32% | 1.34 | 744 |
| 10 | 106.80% | 11.19% | -12.44% | 1.36 | 372 |
| 21 | 95.22% | 11.35% | -14.33% | 1.24 | 177 |
| 63 | 105.56% | 11.85% | -14.79% | 1.28 | 57 |

加上手续费后的对比如表 4-3 所示。

**表 4-3 含交易成本（万分之一佣金）时四种调仓频率的对比**

| 频率 | 累计收益率 | 年化波动率 | 最大回撤 | 夏普比率 | 交易次数 | 总手续费 |
|------|-----------|-----------|---------|---------|---------|---------|
| 5 | 93.36% | 10.91% | -12.97% | 1.27 | 744 | 4,665 元 |
| 10 | 101.68% | 11.19% | -12.81% | 1.31 | 372 | 3,022 元 |
| 21 | 91.93% | 11.35% | -14.50% | 1.21 | 177 | 2,052 元 |
| 63 | 104.09% | 11.86% | -14.85% | 1.27 | 57 | 926 元 |

含成本下的净值曲线对比如图 4-1 所示。

![调仓频率对比（含交易成本）](images/q4-when-to-trade/01-frequency-comparison.png)

### 读懂这些结果

先看表 4-2。四种频率的夏普比率差异不大，从 1.24 到 1.36——不含成本时，每 10 天调仓的夏普比率最高（1.36）。

再看表 4-3。加上手续费后，每 10 天调仓的夏普比率仍然最高（1.31），排名没变。但有一组数字值得关注——最右边两列。交易次数从频率 5 的 744 笔骤降到频率 63 的 57 笔，相差 13 倍；总手续费从 4,665 元降到 926 元，相差 5 倍多。**频率越高，交易越多，成本越大——这是铁律。**

对比表 4-2 与表 4-3 中频率 5 的行：不含成本时累计收益 101.22%，含成本时跌到了 93.36%，手续费吃掉了将近 8 个百分点的收益。而频率 63 呢？从 105.56% 只降到 104.09%，手续费只吃掉了 1.5 个百分点——因为它只交易了 57 笔。

最后看净值图。四条线走势相似，终点差异不算大——频率的选择有影响，但不是决定性的。更重要的发现是：**交易不是免费的**。每次调仓——不管是买还是卖——都要付佣金、交税（根据政策）。在不含成本的“理想世界”里看起来差不多的策略，加上成本后差距就拉开了。

**数据说了算：频率的选择是个权衡——反应速度 vs 交易成本。**

但频率不是最大的问题。想象一下：你刚调完仓，第二天某只 ETF 暴跌 15%。下一次调仓要等好几天——这段时间你只能眼睁睁看着亏损扩大。能不能加一个“紧急出口”——不管调仓日到没到，亏到一定程度就先跑？

---

## 4.2 亏了能不能先跑？

定期调仓的硬伤很明显：两次调仓之间如果市场暴跌，你只能干等。

直觉告诉我们：亏了先跑（卖出止损），总比硬扛着不动好。但“跑”也不是免费的——每次卖出再买回来，都要付手续费。到底值不值？先猜后验。

### 动手实验 2：止损规则对比

我们一起把这份 spec 写出来。这次重点看两件新东西：**累积接续怎么把上一份 spec 的最优结果流到这一份**，以及**为什么这件事必须 Rule 才能做**。

#### 起草上下文 + 任务

> **上下文**：在 `q4-when-to-trade.ipynb` 中已有 RiskParity 策略在不同调仓频率下的回测结果，学员已看到交易成本的影响，理解了“交易不是免费的”。当前问题：定期调仓有个硬伤——中间暴跌只能干等。能不能加个保护？
>
> **任务**：在 notebook 中新建代码单元格，给策略加上止损规则，对比不同止损阈值的效果。

#### 起草要求：累积接续 + Signal vs Rule

止损是 Rule 而不是 Signal——这是 oxq 框架的关键设计，spec 要把这件事掰开讲。

> **要求**：
>
> 1. 先读 oxq 源码：`StopLossRule`（注意它作为 rule 传入 `Engine.run(rules=[...])`，不再是 Strategy 字段）
> 2. 解释 StopLossRule 工作原理：以 `avg_cost x (1 - threshold)` 为止损价提交 stop SELL 挂单 → SimBroker 后续每个 bar 检查是否触发。**为什么 Signal 做不到**：Signal 只看市场数据，不知道你的买入价是多少；只有 Rule 能感知持仓状态
> 3. **用代码动态选**含成本后夏普最高的频率作为基准（不要硬编码）：`BEST_FREQ = max(FREQUENCIES, key=lambda f: results_with_fee[f].sharpe_ratio())`
> 4. 定义 4 组对比：无止损 / threshold=0.05 / 0.10 / 0.15
> 5. 调用 spec-01 的 `run_backtest`，把止损规则作为 `order_rules=[StopLossRule(threshold=T)]` 传入
> 6. 净值曲线对比图（无止损灰色虚线 vs 3 种阈值彩色实线），并在无止损曲线上用浅红色阴影标注最大回撤时段
> 7. 分析按实际数据动态描述，要点出阈值太紧（5%）和太松（15%）的各自问题

> 📌 **要点**：累积接续的关键是**用代码现场算出最优值**而不是硬编码上一份 spec 的结果。`BEST_FREQ = max(FREQUENCIES, key=...)` 这一行让 spec-02 永远跟着 spec-01 的真实数据走——上一份跑出来什么频率最优，这一份就用什么。如果硬编码 `BEST_FREQ = 10`，数据更新后排名变了，基准就错了。**接续型 spec 之间应该用变量传结果，不应该用数字传结果。** **对你的练习**：以后你写第二份 spec 复用第一份的最优值时，宁可写一行 `BEST = max(...)`，也别硬编码具体数字——这样你的 spec 链就能跟着数据自动跑。

> 📌 **要点**：信号（Signal）和规则（Rule）分工不同——这是 oxq 框架的认知基石，后面 q5 / q7 调度多种 Rule 都靠它。spec 要求第 2 条把这层区分写进去，目的是让 AI 在 notebook 里也输出这段解释，给学员第二次曝光。**核心概念应该跨 spec / notebook / book 三处复述，不能只指望 book markdown 单方面承担。** **对你的练习**：以后你 spec 里出现一个不熟的核心概念，要求 AI 在生成的 notebook 里也用一两行打印解释一下——这样你跑代码的时候，notebook 自己就成了你的第二份“小词典”。

#### 起草结果呈现

> **结果呈现**：
>
> 1. StopLossRule 工作原理说明（挂单机制 + Signal vs Rule 的区别）
> 2. 指标对比表（4 行 7 列）
> 3. 净值曲线对比图（含回撤阴影标注）
> 4. 分析文字

完整 spec 在 [`specs/spec-02-stop-loss.md`](https://github.com/xingwudao/xquant-learning/blob/main/q4-when-to-trade/specs/spec-02-stop-loss.md)——复制给 AI，弹窗选「允许」。

AI 助手执行完毕后，你的 notebook 里应该出现了止损规则的工作原理说明、一张对比表和一张带回撤标注的净值图。我们来解读结果。

本节引入了两个新概念。

**止损（Stop Loss）** ——预设一个底线：如果从买入价跌了 X%，自动卖出。就像你去赌场之前跟自己说“今晚最多输 500 块，输到了就走人”——止损就是这个“输到了就走”的自动化版本。“活着才能赚钱”——保住本金，才有机会等到下一次上涨。

**挂单（Pending Order）** ——不是立即交易，而是“预设条件，满足了再执行”。止损规则在你持有某只 ETF 时，自动向券商挂一个条件单：“如果跌到某个价格以下，就帮我卖掉。”你不用每天盯盘，系统帮你盯着。

代码从 Step 1 的结果中自动选出含成本后夏普比率最高的频率（10 天），然后在这个基准上叠加止损规则。四组对比：无止损、5% 止损、10% 止损、15% 止损。

这里有一个关键的新概念。**规则（Rule）** ——策略的“执行层”。第三章的信号（Signal）负责判断“想怎么分”，但信号不知道你的持仓情况——它只看市场数据（波动率、动量），不知道你什么时候买的、买了多少钱、现在是赚还是亏。规则不同——规则能感知持仓状态：知道“我什么时候买的、现在亏了多少”。止损就是这种“看持仓”的判断，只有 Rule 能做。

### 实验结果

止损规则的对比结果如表 4-4 所示（基准频率:每 10 天,含交易成本）。

**表 4-4 止损阈值对比（含交易成本）**

| 止损阈值 | 累计收益率 | 年化波动率 | 最大回撤 | 夏普比率 | 交易次数 | 总手续费 |
|---------|-----------|-----------|---------|---------|---------|---------|
| 无止损 | 101.68% | 11.19% | -12.81% | 1.31 | 372 | 3,022 元 |
| 5% | 100.78% | 10.71% | -13.25% | 1.36 | 389 | 3,903 元 |
| 10% | 100.32% | 11.02% | -13.08% | 1.32 | 378 | 3,270 元 |
| 15% | 95.45% | 11.14% | -14.99% | 1.26 | 374 | 3,071 元 |

净值曲线对比与最大回撤标注如图 4-2 所示。

![止损规则对比（含交易成本）](images/q4-when-to-trade/02-stop-loss-comparison.png)

### 读懂这些结果

先看图 4-2 的大趋势。三条彩色实线（有止损）和灰色虚线（无止损）走势接近——有止损的线在浅红色阴影区域（最大回撤时段）并没有比无止损的线跌得更浅。这是个值得停下来想一想的反直觉信号。

再看表 4-4。三种止损阈值的最大回撤都比无止损更深一点：5% 止损 -13.25%（vs 无止损 -12.81%）、10% 止损 -13.08%、15% 止损 -14.99%——阈值设得越紧，回撤反而没收窄。但夏普比率方向不一样：5% 止损夏普 1.36 反而比无止损的 1.31 高一点，因为它把年化波动率从 11.19% 压到了 10.71%——分母（波动率）下降抵消了分子（收益略降）的影响。

为什么三种止损都没把回撤压住？这不是 bug，而是止损在**持续下跌的市场**中的副作用——**锯齿效应**。

2022 年市场一路缓慢下跌，不是一天暴跌 10%，而是今天跌一点、明天跌一点，连着跌好几个月。5% 止损在这种环境下反复触发，形成一个循环：持仓 —— 跌 5% 触发止损卖出（亏损 + 手续费）—— 调仓日买回来（手续费）—— 继续跌 5% 又触发止损（亏损 + 手续费）—— 调仓日又买回来......

每一轮“卖-买”付两笔手续费，但并没有真正躲开下跌——因为买回来之后市场继续跌。止损只是让你“分批亏”而非“一次亏到底”，额外的交易成本累积起来，反而让总回撤略微加深。从交易次数也能看出端倪：5% 止损多了 17 笔交易，多花了 881 元手续费——这些都是锯齿效应的代价。

**止损对一次性大跌有效（跌到位就跑），对持续缓慢下跌有害（反复卖出又买回来）。在 2021–2025 这段持续震荡下跌的数据里，三种阈值都被锯齿效应反复收割——这不代表止损永远无效，而是说在这个市场环境 + 这套基准上，紧的止损没占到便宜。**

**数据说了算：止损“保护本金”的逻辑没错——夏普比率确实因为波动率下降而抬了一点；但“保护”在这段数据上的含义是“压低波动”而不是“压低回撤”。阈值怎么选、保护的是什么，要看数据怎么说。**

止损解决的是亏损方向。涨的方向呢——下一节继续看。

---

## 4.3 赚了要不要走？

亏的方向有止损兜底了。涨的方向呢——市场给你 10%，你拿着不动，要不要主动伸手去摘？万一涨上去又跌回来，岂不是白赚一场？

直觉告诉我们：赚了就走（卖出止盈），落袋为安，总比看着利润蒸发好。真的吗？先猜后验。

### 动手实验 3：止盈规则对比

我们一起把这份 spec 写出来。这次 spec 的结构和前两份**完全一样**——一样的对比表、一样的净值图、一样的辅助函数。我们要演示的是：**同样的 spec 模板，不同的规则，结果可能截然不同**——结果是什么方向，跑完再揭晓。

#### 起草上下文 + 任务

> **上下文**：在 `q4-when-to-trade.ipynb` 中已有不同调仓频率的对比结果、止损规则的效果（保护了本金，但有成本）。当前问题：止损帮你控制了亏损。反过来——赚了一定程度，要不要主动卖掉、落袋为安？
>
> **任务**：在 notebook 中新建代码单元格，给策略加上止盈规则，对比不同止盈阈值的效果。

#### 起草要求：累积接续到第三层 + 微观证据

这份 spec 在 spec-01（最优频率）+ spec-02（最优止损）的基础上，叠加第三层规则。

> **要求**：
>
> 1. 先读 oxq 源码：`TakeProfitRule`（挂单机制和 `StopLossRule` 类似，只是方向相反——以 `avg_cost x (1 + threshold)` 为止盈价提交 limit SELL 挂单）
> 2. 累积接续：用代码动态选 spec-02 中夏普最高的止损阈值，与 spec-01 的最优频率叠加作为基准 → `BEST_SL = max([sl for sl in STOP_THRESHOLDS if sl is not None], key=lambda sl: results_sl[sl].sharpe_ratio())`
> 3. 定义 4 组对比：不止盈 / threshold=0.10 / 0.20 / 0.30
> 4. 调用 spec-01 的 `run_backtest`，把止损和止盈一起塞进 `order_rules=[StopLossRule(threshold=BEST_SL), TakeProfitRule(threshold=TP)]`
> 5. 净值曲线对比图（不止盈灰色虚线 vs 3 种阈值彩色实线）
> 6. **打印交易记录**（取止盈阈值最小的那组，展示前 10 笔），让学员看到 stop / limit / market 三种 order\_type 在同一回测里共存
> 7. 分析按实际数据动态描述，要点出止损 vs 止盈的对照（止损截断亏损，逻辑清晰；止盈截断利润，越紧伤害越大），并收尾“让利润奔跑，截断亏损”这句行业老话

> 📌 **要点**：累积接续到第三层——`BEST_FREQ`（spec-01）+ `BEST_SL`（本份现场算）+ 4 组止盈阈值。每加一层规则，都是在前一层最优配置之上加。**接续型 spec 链有两种模式：简单复用前一份的产出，以及把前 N 份的最优结果层层叠加。后者就是“累积接续”，适合“逐步给策略加规则”这类研发场景。**

> 📌 **要点**：这是一份**对照实验 spec**——前两份 spec 给学员造的预期是“加规则就会变好”，这一份要做的是把同样的对比表、同样的净值图、同样的辅助函数复用一次，只换一种规则，看数据怎么说。**写对照实验 spec，关键是不要在 spec 里预先暗示结果方向，让数据自己说话。** **对你的练习**：写一份你不知道答案的对照实验 spec 时，反复检查任务段——里面有没有“应该哪个赢”的暗示词（比如“展示 X 的优势”、“证明 Y 有效”）。把这类词都改成中性的“对比 / 评估”，数据才能给你一个干净的答案。这是量化研发里最重要的一类 spec：用数据告诉自己有些规则该不该用。

> 📌 **要点**：要求第 6 条让学员**亲眼看到** stop / limit / market 三种 order\_type 的交易记录前 10 笔。前面所有对比都是宏观指标，这 10 行把规则机制下沉到单笔交易级别。**宏观指标 spec 应该配套微观证据——光看一个总数字不够，要把机制的痕迹露出来给学员看。**

#### 起草结果呈现

> **结果呈现**：
>
> 1. TakeProfitRule 工作原理说明
> 2. 指标对比表（4 行）
> 3. 净值曲线对比图
> 4. 交易记录（前 10 笔，含 order\_type 列）
> 5. 分析文字

完整 spec 在 [`specs/spec-03-take-profit.md`](https://github.com/xingwudao/xquant-learning/blob/main/q4-when-to-trade/specs/spec-03-take-profit.md)——复制给 AI，弹窗选「允许」。

AI 助手执行完毕后，你的 notebook 里应该出现了止盈规则的工作原理说明、一张对比表、一张净值图和一段交易记录。我们来解读结果。

**止盈（Take Profit）** ——预设一个目标：如果从买入价涨了 X%，自动卖出锁定利润。就像你在闲鱼上卖东西，挂一个“到了这个价就成交”的价格——止盈就是给你的持仓挂一个“到了就卖”的目标价。和止损一样，也是通过挂单机制实现的：你不用盯盘，系统帮你盯着。

基准配置是 Step 1 和 Step 2 选出的最优组合：每 10 天调仓 + 5% 止损。在这个基础上，分四组测试：不止盈、10% 止盈、20% 止盈、30% 止盈。止损和止盈可以同时生效。

### 实验结果

止盈规则的对比结果如表 4-5 所示（基准：每 10 天调仓 + 5% 止损，含交易成本）。

**表 4-5 止盈阈值对比（含交易成本）**

| 止盈阈值 | 累计收益率 | 年化波动率 | 最大回撤 | 夏普比率 | 交易次数 | 总手续费 |
|---------|-----------|-----------|---------|---------|---------|---------|
| 不止盈 | 100.78% | 10.71% | -13.25% | 1.36 | 389 | 3,903 元 |
| 10% | 72.72% | 9.70% | -9.47% | 1.18 | 431 | 6,703 元 |
| 20% | 83.47% | 10.04% | -13.25% | 1.26 | 409 | 5,215 元 |
| 30% | 104.32% | 10.23% | -13.25% | 1.45 | 396 | 4,306 元 |

净值曲线对比如图 4-3 所示。

![止盈规则对比（含交易成本）](images/q4-when-to-trade/03-take-profit-comparison.png)

交易记录（10% 止盈，前 10 笔）如表 4-6 所示。

**表 4-6 10% 止盈策略的前 10 笔交易记录**

| 日期 | 标的 | 方向 | 数量 | 成交价 | 订单类型 | 手续费 |
|------|------|------|------|--------|---------|--------|
| 2021-02-01 | 沪深300ETF | BUY | 5,221 | 4.8952 | market | 25.56 |
| 2021-02-01 | 纳指100ETF | BUY | 32,274 | 0.8620 | market | 27.82 |
| 2021-02-01 | 黄金ETF | BUY | 12,204 | 3.8200 | market | 46.62 |
| 2021-02-22 | 沪深300ETF | BUY | 164 | 5.0562 | market | 5.00 |
| 2021-02-22 | 纳指100ETF | BUY | 32 | 0.8974 | market | 5.00 |
| 2021-02-22 | 黄金ETF | SELL | 262 | 3.6750 | market | 5.00 |
| 2021-02-26 | 黄金ETF | SELL | 11,942 | 3.6130 | market | 43.15 |
| 2021-03-08 | 沪深300ETF | SELL | 115 | 4.5957 | market | 5.00 |
| 2021-03-08 | 纳指100ETF | SELL | 241 | 0.8378 | market | 5.00 |
| 2021-03-08 | 黄金ETF | BUY | 12,453 | 3.5190 | market | 43.82 |

### 读懂这些结果

这个结果可能出乎你的意料——**多数止盈阈值降低了收益率，但 30% 止盈反而把收益和夏普都抬了上去。**

先看表 4-5。不止盈时累计收益 100.78%，加了 10% 止盈后骤降到 72.72%，少赚了 28 个百分点；20% 止盈降到 83.47%，也是少赚。但 30% 止盈给了一个反弹——累计收益 104.32%，比不止盈还高 3.5 个百分点；夏普比率从 1.36 升到 1.45，也是四组里最高的。“落袋为安”在不同阈值上给出了完全不同的结论。

为什么 10% / 20% 止盈都在拖后腿？看看我们用的是什么信号。风险平价组合里有三只 ETF，各自承担相同的风险。当某只 ETF 表现好、价格上涨时，它正在为你赚钱。10% / 20% 这种紧的止盈恰好在这个时候把它卖掉——**你亲手切断了正在帮你赚钱的持仓。** 阈值越紧，“切断”得越早，伤害越大。

那 30% 止盈为什么反而抬了一手？阈值放到 30% 之后，止盈极少被触发——表 4-5 里 30% 止盈的交易次数 396 笔，只比不止盈多 7 笔；多花的手续费也只有 403 元。（这 7 笔含止盈触发的卖出 + 触发后下次调仓日的买回；具体止盈触发了几次，可在 notebook 的交易记录里数 `limit` 行。）但少量触发恰好抓住了几次“涨过头又掉头”的极端波动，把波动率从 10.71% 压到 10.23%——分母（波动）小了一点，分子（收益）反而稳了一点，夏普就抬上去了。**这是一个边界值的故事**：阈值太紧，把“正在赚钱的仓”切断；阈值放得够远，止盈基本不动手，少数被触发的恰好是涨过头又掉头的几次，把波动尾部削掉了一点——这就是 30% 止盈在四组里夏普反而最高的原因。

再看手续费那列。不止盈时 3,903 元，10% 止盈跳到 6,703 元——多花了 2,800 元。止盈每触发一次就是一笔额外交易——这一项就把手续费推高了 72%。

表 4-6 的交易记录也很有说明力。注意“订单类型”那列：前 10 笔全是 `market`——也就是再平衡买卖。这意味着这个基准虽然同时挂了 5% 止损 + 10% 止盈两种条件单（一份 `stop` 挂单 + 一份 `limit` 挂单），但前 10 笔里两种都还没被触发。**学员在等的“止盈触发”——`limit` 类型——在 ETF 这种宽基上很少被命中**，但每命中一次都会切断正在涨的持仓。

对比一下 Step 2 的止损：

- **止损截断亏损**——下跌中越拿越亏，跑了是对的
- **止盈截断利润**——上涨中越拿越赚，跑了通常是错的（除非阈值放得够远）

**注意：这个结论是在 2021-2025 年这段数据 + 我们用的风险平价组合上得出的。** 行业里的老话**“让利润奔跑，截断亏损”（Let profits run, cut losses short）** 也是同一个方向——你不知道还会跌多少，先跑为敬；但涨到头没人提前知道，一只 ETF 涨了 10% 后可能还会涨 50%，你在 10% 就卖了，等于亲手把机会让出去。但这句话**不是定理**——换一段时间、换一类标的（比如做短线动量、抓波段反转的策略），止盈在某些场景下可能反而帮了忙。本章给你的不是“止盈一定不要加”这种死结论，而是**“加规则之前，先用数据验证它在你的策略 + 你的时段上到底是帮忙还是添乱”**这个工作方法——这才是带得走的东西。

**数据说了算：不是所有“听起来合理”的规则都有效。每加一条规则，都要用数据验证它到底是帮忙还是添乱——而且要在不同阈值上都看一遍，避免用单一参数的结果下死结论。**

---

## 4.4 回头看：你刚才做了什么？

这一章你做了三件事——给策略加调仓节奏、加止损护栏、加止盈出口。三步走下来，每一步都让你撞了一次“加规则不一定变好”的小墙。下面把三次撞墙打包成三个认知。

三步实验，三个认知升级：

1. **调仓频率**：交易不是免费的。频率越高，交易越多，成本越大。选频率就是在“反应速度”和“交易成本”之间做权衡。

2. **止损规则**：保护本金的逻辑听起来稳——跌破底线就跑。但在 2021–2025 这段持续震荡下跌的数据里，三种阈值都没把回撤压住，只把波动压低了。代价是额外交易和手续费——而且要分清楚“保护”在你的数据上到底是压回撤还是压波动。

3. **止盈规则**：逻辑不像止损那么稳。10% / 20% 阈值都降低了收益率，但 30% 阈值反而把收益和夏普都抬了上去——同一类规则，不同阈值给出反向结论。“落袋为安”听起来合理，但数据告诉我们要谨慎。

你刚才做的事情，在量化交易的框架里叫做**规则设计（Rule Design）** ——在信号的基础上，添加执行层面的保护和约束。我们来看看现在走到了哪里：

- **第二章: 买什么** —— Universe（投资宇宙）
- **第三章: 买多少** —— Signal（信号）——告诉策略每只买百分之多少
- **第四章: 什么时候** —— Rules（规则）——决定何时执行、如何保护 **← 你在这里**

本章把“规则”补进了第三章的“信号”——信号想分钱，规则才动手。两者分工明确，缺一不可。

**一个更深的认知：不是所有“合理”的规则都有效。** 止损的逻辑听起来站得住脚——截断亏损，保护本金，但在我们这段数据上“保护”的含义是压波动而不是压回撤。止盈的逻辑就更模糊——你怎么知道涨到头了？而且换个阈值结论可能反过来。加规则之前，先问两个问题：

- 这条规则的逻辑站得住脚吗？
- 换个参数、换个时段，结论还一样吗？

第二个问题，正是下一章（第五章）要回答的。

---

## 4.5 本章总结

三步实验，从“多久调一次”到“亏了跑不跑”再到“赚了走不走”——你一步步给策略加上了执行层面的规则，也第一次体验到了交易成本的威力和“规则越多越好”这个直觉的破灭。**本章主练的还是“做”——三步都把“动还是不动”从凭感觉变成了可重复执行的规则；而止盈在不同阈值上方向反过来的那一刻，正是“看要指标、还要怀疑你的指标”在飞轮组合层的第一次小预演——下一章你会正式把“看”练成主线。**

### 策略进化路径

本章接着第三章的产出,在 RiskParity 信号上逐步叠加规则——每加一条都用数据验证,而不是凭直觉。

- **第三章产出**：RiskParity 信号 + 每 10 天调仓
  - Step 1: 调频率 —— 频率有影响 + 交易成本不可忽视
  - Step 2: 加止损 —— 压低了波动，但回撤被锯齿效应反复收割
  - Step 3: 加止盈 —— 多数阈值降低收益，30% 阈值反而抬高夏普
- **第四章产出**：完整的规则配置 + “每条规则都要在不同阈值上验证”的认知

### 概念速查表

本章涉及的核心概念汇总如表 4-7 所示,方便随时回查。

**表 4-7 第四章核心概念速查**

| 概念 | 含义 | 类比 |
|------|------|------|
| 交易成本（Transaction Cost） | 每次买卖都要付的费用——佣金（付给券商）+ 税（付给政府） | 去银行换汇要收手续费 |
| 止损（Stop Loss） | 预设底线：从买入价跌了 X% 就自动卖出 | “今晚最多输 500，输到了就走” |
| 止盈（Take Profit） | 预设目标：从买入价涨了 X% 就自动卖出 | “到了这个价就成交” |
| 挂单（Pending Order） | 预设条件，满足了再执行的订单 | “到价自动触发” |
| 锯齿效应（Whipsaw） | 在持续下跌的市场里反复“卖出—回涨—又跌—又卖”，每来回一次付一笔成本 | 像锯子来回切，越切越薄 |
| 规则（Rule） | 策略的执行层——感知持仓状态，决定何时动手 | 信号是大脑，规则是手脚 |
| 让利润奔跑，截断亏损 | 止损截断亏损（通常有帮助），别用止盈截断利润 | 交易经典格言 |

### 本章最重要的收获

走完三个实验后,本章最值得带走的核心认知归纳如表 4-8 所示。

**表 4-8 第四章核心认知**

| 认知 | 来源 |
|------|------|
| 交易不是免费的——每次买卖都有成本 | 4.1 |
| 调仓频率是反应速度与交易成本的权衡 | 4.1 |
| 止损压低了波动，但回撤可能被锯齿效应反复收割 | 4.2 |
| 信号看市场，规则看持仓——两者角色不同 | 4.2 |
| 止盈结论随阈值反向——单一参数不能下死结论 | 4.3 |
| 不是所有“合理”的规则都有效，要在不同阈值上用数据验证 | 4.3 |
| 让利润奔跑，截断亏损（经验，不是定理） | 4.2 + 4.3 对比 |
| 先猜后验，数据说了算 | 贯穿全章 |

### 带走的问题

读完本章,你应该带着两个挥之不去的疑问进入下一章——它们正是第五章要回答的:

- 策略看起来不错，但**怎么知道不是恰好在这段时间表现好？** 换个时间段结果还一样吗？→ 第五章
- 回测结果漂亮就能赚钱吗？**怎么避免自欺欺人？** → 第五章

> 本章所有代码的可运行版本见 `notebooks/q4-when-to-trade.ipynb`

---

## 拓展阅读

本章正文聚焦“调仓频率 / 止损 / 止盈”三个执行规则，下面这段是动手之前可以省略的视野铺垫——读完正文后回头看，反而吸收得更快。

### A 股 / 美股 / 港股交易成本对照

不同市场的费率结构有所不同。读者按自己实际交易的市场对照即可，回测里只要把它们都算进 `PercentageFee` 模型就好。三个市场的常见费率对照如表 4-9 所示。

**表 4-9 三大市场常见交易成本对照**

| 市场 | 佣金（付给券商） | 税（付给政府） | 备注 |
|------|----------------|--------------|------|
| A 股 | 万分之一 ~ 万分之三，最低 5 元 | 卖出收千分之一印花税（买入不收） | 还有过户费（沪市）等小项目，多数券商给打包简化 |
| 美股 | 按笔（如 0.5 ~ 1 美元）或按股（每股 0.005 美元起），各家不同 | 没有印花税；卖出有 SEC 费 + FINRA TAF（按金额比例，极小） | 多数零佣金券商以 PFOF（订单流付费）或点差变相补偿 |
| 港股 | 万分之五左右，部分券商更低 | 买卖双向各 0.13% 印花税 | 还有 0.005% 证监会征费、0.0027% AFRC 征费等小项目 |

**怎么估你的实际成本？** 把“佣金 + 税 + 滑点（先按 0.1% 估）”加起来，就是你单次往返的预算。回测里把这个数填进 `PercentageFee.rate`，结果会更接近实盘。

### 为什么“让利润奔跑，截断亏损”是格言而不是定理

本章 4.3 你已经看到，10% / 20% 这种紧的止盈阈值都让累计收益下降了——多数情况下“落袋为安”输给了“让利润奔跑”；只有放到 30% 这种很少触发的阈值，才偶尔把波动压低、夏普反弹。但这并不意味着任何场景下都不该止盈。**这条格言适用的是“中长周期、跟趋势”的策略，比如本章用的风险平价 + 中频再平衡。** 如果你跑的是短线波段、做反转、或者高杠杆策略，止盈往往是必要的——不止盈就可能在一次回调里把所有浮盈吐光。换句话说：**格言是经验的提炼，不是定理；它把概率告诉你，但需要你结合策略类型 + 阈值去用。**

---

## 反馈与读者群

如果你发现本章有错别字、概念不清或实验结果不一致，欢迎
[提交 GitHub Issue](https://github.com/xingwudao/xquant-beginner/issues/new/choose)。

课程、正式书和后续更新见
[xquant.shop/courses](https://xquant.shop/courses)。

添加作者微信 `xquanter` 进入读者群。请备注：`XQuant 读者`。
