Files

420 lines
34 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第 3 章:决定各买多少:3 种分法实测
> 最新书稿已更新至 [XQuant 量化课堂页](https://xquant.shop/courses)。
> 想阅读最新版官方书稿,请前往图书页。
第 2 章我们从全市场几千个标的中,一步步筛选出了三只 ETF:沪深 300、纳指 100、黄金,构建了自己的标的池。选资产的问题解决了,但紧接着一个新问题摆在面前:三只 ETF,各买多少?
这听起来像个分钱小问题:平均分一下不就完了吗?别急着下这个结论。同样三只 ETF、同一段历史,只是分法不同,本章这段历史数据里的累计收益确实差出 1 倍以上。买什么,决定你参与哪些市场;买多少,决定每个市场对账户的影响有多大。这就是为什么一整章都要解决这个问题。
### 路线图
本章进入四个阶段里的**制定规则**阶段。本章分三步探索三种分钱方式,每节做 1 次实验,最后你来判断哪种更合理。全章一共要动手做 3 次实验。
**表 3-1 第 3 章路线图**
| 节 | 内容 | 实验 |
|----|------|------|
| 3.1 | 最简单的回答:各买三分之一 | 1 |
| 3.2 | 波动大的少买点,波动小的多买点 | 1 |
| 3.3 | 涨得好的多买点,跌的别买 | 1 |
---
## 3.1 最简单的回答:各买三分之一
三只 ETF,各买多少?最自然的回答:平均分,每只买三分之一。不偏不倚,公平合理。毕竟,如果你不确定哪只会涨得更好,平均分至少不会犯大错。对吧?
我们来验证一下这个直觉。
### 动手实验 1:等权组合回测
这个实验用到 open-xquant 的几个模块:`EqualWeightOptimizer`(把钱平均分的组合优化器)、`Engine`(回测引擎,模拟历史买卖)、`SimBroker`(模拟券商)。你不需要了解它们的代码,spec 会告诉 AI 怎么用。
我们一起把这份 spec 写出来。这次重点看三件新东西:**oxq 模块的“先读后写”**、**权重和 = 1.0 的关键断言**、**将多只资产的交易日对齐**。
#### 第一、二段:上下文和任务描述
这是 q3 的第一份 spec,直接接续第 2 章。三只 ETF 已经选好了,现在只关心权重。上下文说前提,任务描述说要做什么,两段都很短:
> **上下文**:第 2 章选好了 3 只 ETF(沪深 300、纳指 100、黄金),现在要回答:每只各买多少?
>
> **任务描述**:在 notebook `q3-how-much.ipynb` 中创建代码,跑等权组合回测,并展示三只 ETF 各自的表现差异。
#### 第三段:任务要求
spec 主体的第一件事不是写代码,而是要求 AI 先读源码。这样它调的是真实接口,不会用过时或编造的写法。
> **任务要求**
>
> 1. 阅读以下 oxq 模块的源码(按用途分四组):
> - 引擎与策略:`oxq.core.Engine` / `Strategy`
> - 数据:`oxq.data.YFinanceDownloader` / `LocalMarketDataProvider`
> - 权重分配:`oxq.portfolio.optimizers.EqualWeightOptimizer`
> - 信号、指标、规则:`oxq.signals.Threshold` / `oxq.indicators.RollingVolatility` / `oxq.rules.RebalanceFrequencyRule` / `oxq.universe.StaticUniverse` / `oxq.trade.SimBroker`
> 2. 下载 3 只 ETF`SYMBOLS = ("510300.SS", "513100.SS", "518880.SS")`,起 `2021-01-01`)。
> 3. 计算共同交易日:把三只 ETF 的索引取交集,后续所有图都用这组对齐后的日期。
> 4. 用 `EqualWeightOptimizer` + `Threshold` 信号(column="close", threshold=0, relationship="gt")组装策略,再平衡 `RebalanceFrequencyRule(interval_days=10)`。
> 📌 **要点**:多只资产放在一起比较时,先要把日期对齐。即使三只 ETF 都在 A 股市场交易,历史数据也可能出现个别缺失日。spec 要求先取共同交易日,是为了让三条净值曲线站在同一组日期上比较,避免图表因为日期不齐而产生误导。
#### 第四段:验收标准
验收标准段把权重检查、对比表、净值曲线一次锁死,让 AI 输出可以直接对照书里的图表。
> **验收标准**
>
> 1. **权重合法性检查**`weights = ew_weights_df.iloc[-1]; assert abs(weights.sum() - 1.0) < 1e-6, f"权重和应为 1.0,实际 {weights.sum()}"; assert (weights >= 0).all(), "等权权重不应为负"`。
> 2. 打印权重分配 + 10 万元换算(每只约 33,333 元)。
> 3. 打印 4 行对比表:沪深 300 / 纳指 100 / 黄金 / 等权组合,三列指标(累计收益率、年化波动率、最大回撤)。
> 4. 净值曲线图:3 条灰色线(用 `--` / `:` / `-.` 三种线型区分单只持有)+ 1 条蓝色实线加粗(等权组合),统一从 100 开始,标题「各买三分之一:等权组合 vs 单只持有」。
> 📌 **要点**:权重分配 spec 里必须写清一个验收标准:「权重和 = 1.0 ± 1e-6」。这在第 1 章那种单资产策略里不需要。只有一个资产时,要么买,要么不买。但只要进入“分钱”场景,这条就必须有。没有这条断言,AI 输出 0.998 或 1.003 你可能都会以为正常。下游回测会按这个权重直接乘资金,误差会一路传到最后的收益率。三个 spec 都要复用这条模板。
完整示例 spec 在配套仓库的 [`q3-how-much/specs/spec-01-equal-weight.md`](https://github.com/xingwudao/xquant-learning/blob/main/q3-how-much/specs/spec-01-equal-weight.md)。参考它确认自己的 spec 后,再复制给 AI,按提示允许执行。
AI 执行完毕后,你的 notebook 里应该出现了一组权重数字、一张对比表和一张净值图。我们先认识两个新概念。
**权重(Weight**:回答“每个标的分总资金的多少比例”。每个标的分到总资金的百分之多少,就是它的权重。你有 10 万元,三只 ETF 各 1/3 权重,就是每只买 33,333 元。
**权重分配方法**:回答“按照什么逻辑分配资金”。等权分配就是一种最简单的权重分配方法:不看涨跌,不看波动,三只 ETF 各 1/3。权重分配方法有很多,后面也可以看到不同的分配方式。至于目标权重之后怎么变成真实订单、按什么价格成交,那是第 7 章要处理的事。
在量化领域,在一起参与分配资金的一组标的,叫做**组合(Portfolio)**。组合不是简单的候选名单。第 2 章选出的标的池,是策略允许考虑的范围,是候选大名单;第 3 章要做的,是从这个大名单里给每只标的分配资金。按规则算出来的配置资金比例,叫**目标权重(Target Weight**。
目标权重只是理想资金比例。账户里真正持有的标的和份额数量,叫**持仓(Position)**;这些持仓按市值算出来的比例,才是**实际权重**。即使一开始完全按目标权重买入,后面标的价格也会涨跌,实际权重会慢慢偏离目标权重。因此,过一段时间就要重新调整持仓,让实际权重重新接近目标权重。这个过程叫**再平衡(Rebalancing**。
标的池里的标的,不是每次都一定能分到钱。有些标的的权重可能是 0,极端情况下,所有标的的权重都可能是 0。这时可以把现金(CASH)也视为组合成员,现金获得 100% 权重,意味着账户里暂时不持有任何标的。
### 实验结果
等权就是强制让资产的权重相等,三只各占三分之一,每只买 33,333 元。这种平均分资金的方式,效果如何呢?三只 ETF 单独持有与等权组合的对比如表 3-2 所示。
**表 3-2 三只 ETF 单独持有 vs 等权组合表现**
| ETF | 累计收益率 | 年化波动率 | 最大回撤 |
|------|-----------|-----------|---------|
| 沪深300ETF | -1.78% | 18.11% | -42.16% |
| 纳指100ETF | 106.09% | 22.66% | -27.50% |
| 黄金ETF | 188.54% | 15.83% | -16.77% |
| **等权组合** | **80.75%** | **11.73%** | **-16.79%** |
净值曲线对比如图 3-1 所示。
![各买三分之一:等权组合 vs 单只持有](images/q3-how-much/01-equal-weight-nav.png)
### 读懂这些结果
先看图 3-1 的大趋势:等权组合那条蓝色实线,走在三条灰色线之间。它不是最好的,也不是最差的。黄金一路领涨,纳指 100 先跌后涨、波折不断,沪深 300 整体低迷。组合把三者的表现“平均”了一下,走出了一条居中的路线。
但你可能已经注意到了:黄金单独拿的累计收益率是 188.54%,比等权组合的 80.75% 高了一倍多。那为什么不全买黄金?
看波动率那列。**年化波动率(Annualized Volatility** 衡量的是价格上下波动的剧烈程度。第 2 章我们认识过这个指标。波动率大,意味着你的账户大起大落:今天涨 3%、明天跌 4%,反复折腾。现实中大部分人受不了这种颠簸。涨的时候开心,连跌几天就开始怀疑“是不是该卖了”,一卖可能就错过后面的反弹。**波动率小,意味着你能在低点不慌、在上涨中不慌。** 账户里一时少了 5%、10%,你不会马上卖掉。不慌,才能真正拿到那段收益。
再看一个更关键的问题。三只 ETF 的年化波动率从 15.83%(黄金)到 22.66%(纳指 100),差了好几个百分点。但等权方法说:“不管你多颠簸,都买一样多的钱。”这意味着什么?波动最大的纳指 100,虽然只花了 1/3 的钱,但它每天的涨跌幅度最大,对组合整体的影响也最大。它一跌,组合就被拖着跌;它一涨,组合的涨幅也主要靠它撑着。**每个标的拿一样多的资金,并不等于它造成的波动也一样。** 波动大的那只,用同样的钱产生了更大的影响力,拉着整个组合的涨跌走。
有没有更合理的分法?比如,波动大的少买点,波动小的多买点,让每只 ETF 对组合的“影响力”更接近?
---
## 3.2 波动大的少买点,波动小的多买点
上一节的结论很清楚:平均分钱,并不等于平均分影响力。波动大的纳指 100,用同样的钱却产生了更大的影响力,把整个组合的涨跌拉着走。那反过来想:既然波动大的影响力太大,能不能给它少分点钱?波动小的那只影响力不够,能不能多分点钱给它?这样三只 ETF 对组合的“拉扯力度”应该更均匀。
这种“按波动率倒数分配权重”的方法,有一个正式的名字:**风险平价(Risk Parity)**。本章用的是简化版风险平价:波动大的少买,波动小的多买,让每只标的对组合的影响更均匀。你可以把它想成把三个发动机的功率调到接近,整个组合就不容易被某一个拖着走。
想象你要把一笔钱交给三个人帮你打理。第一个人成熟稳重,赚了不炫耀,亏了不慌张,你看着他的账户心里很踏实;第二个人情绪起伏大,今天信心很足,明天就想清仓;第三个人介于两者之间。你会怎么分钱?大概率会让稳重的那个多管点,让情绪起伏大的那个少管点。三只 ETF 也一样。波动率就是它们的“情绪起伏程度”。波动小的稳重,多买;波动大的起伏大,少买。
### 动手实验 2:风险平价组合回测
我们一起把这份 spec 写出来。这次重点看两件新东西:**`signal.required_indicators` 在信号上挂指标**、**公共配置复用,只改差异点**。
#### 第一、二段:上下文和任务描述
这是 q3 的第二份 spec,接续 spec-01 的等权回测结果。上下文一句话承接,任务一句话点明:
> **上下文**:在 `q3-how-much.ipynb` 中已有等权组合回测结果。读者已看到三只 ETF 波动率差异很大,等权分配让波动大的 ETF 主导了组合涨跌。当前问题:能不能按波动来分:波动大的少买,波动小的多买?
>
> **任务描述**:在 notebook 中新建代码单元格,用风险平价权重分配方法运行回测,对比等权组合。
#### 第三段:任务要求
风险平价要用波动率,这就要定义波动率指标,并且要使用 oxq 提供的接口告诉风险平价这个权重分配模块,使用这个指标。
> **任务要求**
>
> 1. 阅读 `oxq.portfolio.optimizers.RiskParityOptimizer`(若 spec-01 未读 `Threshold`,补一下)。
> 2. 在 `Threshold` 信号上注册 vol 指标:`signal_rp.required_indicators = {"vol": (RollingVolatility(), {"column": "close", "period": 20})}`。这样回测引擎跑到信号时会自动算 vol 列,优化器再读这一列分配权重。
> 3. 用 `RiskParityOptimizer(volatility_col="vol")` 装配优化器,打印最新一天与等权权重的对比 + 10 万元换算。
> 4. 抽出公共配置 `COMMON = dict(universe=universe, ...)`,让等权和风险平价两个策略只在 `portfolio` 一行不同:`Strategy(name="equal-weight", **COMMON, portfolio=EqualWeightOptimizer())` vs `Strategy(name="risk-parity", **COMMON, portfolio=RiskParityOptimizer(...))`。
> 📌 **要点**:这里的 `signal.required_indicators` 是 open-xquant 的代码入口,用来告诉回测引擎:先算出 vol 指标,再让风险平价方法读取 vol 来分配权重。读者不需要记接口细节,但 spec 要把“风险平价依赖波动率”这件事写清楚。
#### 第四段:验收标准
这一段把“权重检查 + 图表要求”两件事一起写。
> **验收标准**
>
> 1. **权重检查**:复用 spec-01 的 assert 模板,加一条`assert rp_w.nunique() > 1, "风险平价权重不应全相等(否则退化为等权)"`。
> 2. 权重对比表 + 10 万元换算 + 指标对比表。
> 3. 净值曲线对比图(figsize 12×6,等权灰虚 + 风险平价蓝实) + 权重历史堆叠面积图(figsize 12×4,展示三只 ETF 权重随时间变化)。
> 4. 末尾分析文字按数据动态描述方向(若波动率反升说明 vol 估计期偏短,不要硬写“降到”)。
> 📌 **要点**:权重分配可视化采用了**权重历史堆叠面积图**。它让人一眼看到“权重不是一成不变的”。等权下三条带等宽,风险平价下面积带宽随时间变化。
完整示例 spec 在配套仓库的 [`q3-how-much/specs/spec-02-risk-parity.md`](https://github.com/xingwudao/xquant-learning/blob/main/q3-how-much/specs/spec-02-risk-parity.md)。参考它确认自己的 spec 后,再复制给 AI,按提示允许执行。
AI 执行完毕后,你的 notebook 里应该出现了新的权重数字、一张对比表、两张图。
风险平价方法和 3.1 的等权方法用法相似,只是换了 `portfolio` 里的权重分配方法,其余不变。但钱真的分得不一样了:等权是三等分,风险平价则是波动小的多分、波动大的少分。同样 10 万元,沪深 300ETF 从 33,333 元涨到了 45,510 元,黄金 ETF 从 33,333 元降到了 11,239 元。
策略组装和上一节几乎一模一样。整个策略定义只有 `portfolio` 那一行不同,从 `EqualWeightOptimizer()` 换成 `RiskParityOptimizer()`。其余的全部复用。当框架搭好之后,换一种“分钱方式”就只是换一个权重分配方法。
### 实验结果
两种方案的权重对比如表 3-3 所示。这里有一个细节:表 3-3 的权重按**最近 20 天的滚动波动率**算,与表 3-2 那行**全期年化波动率**不是同一组数字。这两个指标反映的是短期波动与长期波动。
**表 3-3 等权 vs 风险平价权重对比**
| ETF | 等权权重 | 风险平价权重 |
|------|---------|------------|
| 沪深300ETF | 33.3% | 45.5% |
| 纳指100ETF | 33.3% | 43.3% |
| 黄金ETF | 33.3% | 11.2% |
从指标看,两种方案的差异如表 3-4 所示。
**表 3-4 等权 vs 风险平价指标对比**
| 指标 | 等权组合 | 风险平价组合 |
|------|---------|------------|
| 累计收益率 | 80.75% | 99.67% |
| 年化波动率 | 11.73% | 10.81% |
| 最大回撤 | -16.79% | -12.26% |
净值曲线对比如图 3-2 所示,权重随波动率变化的历史轨迹如图 3-3 所示。
![等权 vs 风险平价](images/q3-how-much/02-ew-vs-rp-nav.png)
![风险平价:权重如何随波动率调整](images/q3-how-much/03-rp-weight-history.png)
### 读懂这些结果
先看图 3-2 的净值曲线。蓝色实线(风险平价)走得比灰色虚线(等权)更平滑,而且终点更高。这说明,风险平价不仅更稳,还赚得更多。
再看指标表里的数字。波动率从 11.73% 降到 10.81%,最大回撤从 -16.79% 收窄到 -12.26%。最惨的时候少跌了 4.5 个百分点。收益率反而更高了(80.75% → 99.67%)。在这段数据里,按波动调整不仅降低了波动,也保住了收益。对真实投资者来说,波动小还有一个好处:更容易坐得住,因此也许更能享受到后面的涨势。
最后看图 3-3 的权重历史。你会发现一个有意思的现象:三只 ETF 的权重不是固定的,而是随时间在动态调整。某段时间黄金 ETF 波动率飙升,它分到的钱就自动减少;某段时间沪深 300ETF 变得平稳了,它分到的钱就自动增加。这就是节首讲的“按波动率倒数分配”在跑。市场数据在告诉你谁该多买、谁该少买,你不用做主观判断。
风险平价比等权更稳了。但你有没有注意到,它只看波动率,完全不看涨跌。如果一只 ETF 正在快速上涨,风险平价不会多买它;如果一只正在下跌,也不会少买它。能不能更强调上涨趋势,让涨得好的多买点?
---
## 3.3 涨得好的多买点,跌的别买
风险平价让组合更稳了,但回头想想,它的判断标准只有一个:波动率。波动大的少买,波动小的多买,仅此而已。至于一只 ETF 是在涨还是在跌?它完全不关心。黄金如果正在快速上涨,风险平价不会因此多买一分钱;沪深 300 如果正在明显下跌,它也不会少买一分钱。它只看颠簸程度,不看方向。
那能不能更进取一点:涨得好的多买,跌的干脆不买?我们可以借鉴一种交易思想:趋势交易。顾名思义,也就是跟着涨势走。动量排名就是趋势交易思想的量化表达:过去一段时间涨得好的,我认为它可能继续上涨,多买;过去一段时间下跌的,我认为它可能继续走弱,不买。
### 动手实验 3:动量排名组合回测
我们一起把这份 spec 写出来。这次重点看两件新东西:**多指标依赖链**、**过滤后重新把权重分到 100%**。
#### 第一、二段:上下文和任务描述
这是 q3 三联 spec 的最后一份,从“始终持有全部 ETF”走到“动量为负的不买”。上下文 + 任务一气呵成:
> **上下文**:在 `q3-how-much.ipynb` 中已有等权组合和风险平价组合的回测结果。读者已理解:等权不看任何指标,风险平价只看波动率。当前问题:风险平价更稳了,但它完全不看涨跌。能不能让涨得好的多买点?
>
> **任务描述**:在 notebook 中新建代码单元格,用动量排名权重分配方法运行回测,与前两种方案对比。
#### 第三段:任务要求
动量排名比风险平价多一步:它要先计算“动量 / 波动率”,作为权重分配要依赖的指标。而这个指标又依赖动量和波动率两个指标。oxq 支持这种链式指标依赖,也支持这种排序型权重分配。
> **任务要求**
>
> 1. 阅读 `TopNRankingOptimizer` / `Momentum` / `Ratio` 三个 oxq 模块。
> 2. 在信号上注册三个指标(指标依赖链):
> ```python
> signal_tn.required_indicators = {
> "mom": (Momentum(), {"column": "close", "period": 20}),
> "vol": (RollingVolatility(), {"column": "close", "period": 20}),
> "ram": (Ratio(), {"col_a": "mom", "col_b": "vol"}),
> }
> ```
> 顺序很重要。ram(风险调整后的动量指标)依赖 mom(动量)和 vol(波动率,代表风险),所以 mom 和 vol 要先注册。
> 3. 用 `TopNRankingOptimizer(score_col="ram", n=3, filter_negative=True)` 装配优化器。`n=3` 是因为标的池里就 3 只 ETF,这里等于“全选 + 过滤负值”;`filter_negative=True` 是动量排名的硬规则:**正在跌的不买**。
> 4. 复用 spec-02 的 COMMON 字典,只把 portfolio 换成 `TopNRankingOptimizer(...)`。
> 📌 **要点**:多指标依赖链是 oxq 的关键能力。spec-02 注册 1 个指标(vol),spec-03 注册 3 个指标,而且 ram 依赖前两个。spec 必须把注册顺序写清楚,而不是只列指标名。
> 📌 **要点**:排序型 spec(动量排名)与加权型 spec(等权、风险平价)的本质区别是:可以**放弃部分 ETF**。所以“权重和 = 1.0”的断言要改成“未被过滤的活跃权重和 = 1.0”:`active_w = tn_w[tn_w > 1e-6]; assert abs(active_w.sum() - 1.0) < 1e-6`。这一步是在检查:被留下来的 ETF,权重是否重新分满 100%。加权型 spec 不需要这一步。
#### 第四段:验收标准
这一段有个交付细节:把“动量为负时面积图留出空白”,这是一个特殊图表要求,写进 spec。
> **验收标准**
>
> 1. **权重检查**:用排序型断言(过滤后重新分到 100%)。
> 2. 三种方法权重对比表 + 三种方法指标对比表。
> 3. 三条净值曲线图(等权灰虚 + 风险平价蓝实 + 动量排名橙实)。
> 4. 动量排名权重历史堆叠面积图,**注意:动量为负时权重为 0,面积会出现空白**。把这个视觉特征预先写进 spec。
> 5. 分析文字按实际数据动态描述方向,不要硬写“更大”或“更深”。市场不可预测,spec 不应假设结果方向。
> 📌 **要点**:可视化 spec 不仅要描述要画什么,还要把特殊图表现象预先点出,比如这里的:动量为负时面积图会出空白。
完整示例 spec 在配套仓库的 [`q3-how-much/specs/spec-03-momentum-ranking.md`](https://github.com/xingwudao/xquant-learning/blob/main/q3-how-much/specs/spec-03-momentum-ranking.md)。参考它确认自己的 spec 后,再复制给 AI,按提示允许执行。
AI 执行完毕后,你的 notebook 里应该出现了新的权重对比、一张三种方法的指标表、两张图。这个实验引入了两个新概念。
**动量(Momentum**:过去 N 天的涨跌趋势。正数代表上涨,负数代表下跌。就像看一辆车的速度表,动量为正说明车在向前,动量为负说明车在后退。这里用的是 20 日动量,也就是看过去一个月的涨跌方向。
但单看动量有个问题:波动大的标的天然动量值更大。它涨 5%,波动小的标的涨 2%,可能只是因为它“脾气大”,不一定代表趋势更强。所以我们把动量除以波动率,得到**风险调整动量(ram = mom / vol)**。你可以把它理解成每份波动带来的动量。这样不同波动程度的标的之间才能公平比较。
**动量归一化权重分配方法**:按 ram 值排名分配权重。ram 为负的 ETF 直接权重设为 0,也就是不买。剩下的 ETF 再按 ram 大小重新分配到 100%。涨得越好且波动越小,分到的钱越多。如果三只 ETF 里有两只 ram 为负,那所有的钱就全部分到唯一 ram 为正的那只上。
### 实验结果
三种权重分配方法的权重对比(最新一天)如表 3-5 所示。
**表 3-5 三种权重分配方法最新一天权重对比**
| ETF | 等权 | 风险平价 | 动量排名 |
|------|------|---------|---------|
| 沪深300ETF | 33.3% | 45.5% | 0.0% |
| 纳指100ETF | 33.3% | 43.3% | 0.0% |
| 黄金ETF | 33.3% | 11.2% | 100.0% |
差异一目了然。等权三等分,风险平价按波动调整,而动量排名直接把沪深 300 和纳指 100 的权重归零。策略运行时,它们在跌,所以不买。所有的钱全部分到唯一在涨的黄金 ETF。
三种权重分配方法的指标对比如表 3-6 所示。
**表 3-6 三种权重分配方法指标对比**
| 指标 | 等权 | 风险平价 | 动量排名 |
|------|------|---------|---------|
| 累计收益率 | 80.75% | 99.67% | 179.96% |
| 年化波动率 | 11.73% | 10.81% | 15.54% |
| 最大回撤 | -16.79% | -12.26% | -15.77% |
三条净值曲线同框对比如图 3-4 所示,动量排名的权重历史如图 3-5 所示。
![三种权重分配方法对比:等权 vs 风险平价 vs 动量排名](images/q3-how-much/04-three-signals-nav.png)
![动量排名:涨的多买,跌的不买](images/q3-how-much/05-momentum-weight-history.png)
### 读懂这些结果
先看图 3-4 的净值曲线,三条线同框对比。橙色实线(动量排名)终点最高,累计收益 179.96%,远超风险平价的 99.67% 和等权的 80.75%。但这笔更高收益不是白来的:动量排名的波动率是 15.54%,高于风险平价的 10.81%;最大回撤 -15.77%,也深于风险平价的 -12.26%。**收益更高,但颠簸更大、回撤更深,这就是更进取方法的代价。**
别急着下结论。记得第 1 章的教训吗?同一个策略换个时间段,结果可能完全不同。动量排名在这几年的数据上表现优异,是因为市场恰好有几段清晰的趋势,比如黄金从 2023 年开始的持续上涨。换段时间,动量排名的判断可能跟不上市场反转的节奏。这种“在这段数据上好看,换一段时间就出问题”的现象,第 5 章会专门讨论,本章先不下结论。没有完美的分法。风险平价更稳,动量排名更进取。哪个更适合你,要看你打算拿多久、能承受多大波动、能不能扛得住一段时间的回撤。第 5 章我们会用更系统的方法判断“一个策略到底好不好”。
再看图 3-5 的权重历史。对比 3.2 的风险平价权重图,动量排名的变化剧烈得多。某只标的动量转负,权重直接从几十个百分点归零,钱转向其他标的;等它动量转正,权重又突然变高。你还会发现图中有些时段出现了空白:三只标的的权重加起来不到 100%,甚至全部归零。这说明那段时间三只 ETF 的动量全是负的,没有一只在涨,这种权重分配方法给出的决策是“都别买”。风险平价的权重变化则平缓得多,三只 ETF 始终都持有,只是比例在缓慢调整。两种权重分配方法的特点完全不同:一个求稳,一个求进。风险平价像一个谨慎的管家,均匀分配、避免偏科;动量排名像一个进取的交易者,押注走势更好的资产,果断放弃走弱的资产。
三种方法,三种结果。到这里,你可能已经有了一个模糊的感觉:刚才做的事情好像不简单。选权重分配方法、跑回测、比指标,这不就是在做量化研究吗?
---
## 3.4 回头看:你刚才做了什么?
三次实验,你只换了一样东西:权重分配方法。数据、标的池、回测引擎、再平衡频率全部一样,只有“钱怎么分”不同,结果就完全不同。
你刚才做的事情,可以叫**权重分配方法对比**:保持其他条件不变,只切换“钱怎么分”,观察结果差异。这是量化研究中最基础、也最重要的操作之一。权重分配方法只给出目标权重,不负责决定什么时候调整持仓。什么时候调整、什么条件下卖出,这是规则(Rules)的事,第 4 章会讲。怎么把目标权重变成真实订单、按什么价格成交,是第 7 章会讲的执行交易问题。
我们来看看你现在走到了哪里:第 2 章解决了“买什么”(标的池),第 3 章解决了“买多少”(权重分配),第 4 章将解决“什么时候动手”(Rules)。
第 1 章里的 Crossover 信号说的是“买还是不买”:价格上穿均线就买、下穿就卖,要么买,要么不买。第 3 章的三种权重分配方法说的是“每只买百分之多少”:从 0% 到 100%,可以精确到小数点。这两类东西不要混在一起。信号负责判断市场状态,权重分配方法负责把钱分到资产上。三种权重分配方法的特点如表 3-7 所示。
**表 3-7 三种权重分配方法的特点对照**
| 方法 | 看什么 | 特点 |
|------|--------|------|
| 等权 | 什么都不看 | 简单平均 |
| 风险平价 | 波动率 | 更重视平稳 |
| 动量排名 | 涨跌趋势 | 更重视上涨 |
“性价比”这个问题来了。三种方法的收益和波动都不同,能不能用一个数字综合衡量?这是下一节要回答的事。
---
## 3.5 夏普比率:一个数字概括“性价比”
三种权重分配方法的收益率和波动率都不同。等权收益最低,风险平价更稳且收益更高,动量排名收益最高但波动也最大。有没有一个数字能把收益和波动综合起来,告诉你“每承受一份颠簸,能赚多少收益”?
有。**夏普比率(Sharpe Ratio)** 的标准定义是:(年化收益率 - 无风险收益率)/ 年化波动率。它衡量的是:每承担一份波动,能比无风险资产多获得多少额外收益。
本章先让你理解“收益 / 波动”的关系,所以暂时采用简化口径:年化收益率 / 年化波动率。严格说,这不是标准夏普值,而是一个近似的收益波动比。等你真正比较策略时,应该使用扣除无风险收益后的标准夏普比率。三种方法的简化夏普对比如表 3-8 所示。
**表 3-8 三种权重分配方法的简化夏普对比**
| 指标 | 等权 | 风险平价 | 动量排名 |
|------|------|---------|---------|
| 年化收益率 | 11.96% | 13.97% | 20.80% |
| 年化波动率 | 11.73% | 10.81% | 15.54% |
| 简化夏普 | 1.08 | 1.35 | 1.42 |
看最后一行。三种方案的简化夏普依次递增:等权 1.08,风险平价 1.35,动量排名 1.42。风险平价比等权高出不少,说明按波动调整确实提升了性价比。动量排名最高,但优势不如收益率差距那么大,因为它的波动率也更高,部分抵消了收益优势。不过,夏普比率有三个你需要知道的局限:
1. 它把上涨和下跌的波动一视同仁,但你可能根本不介意往上的颠簸。
2. 同一个策略换个时间段,夏普比率可能完全不同。它只反映特定历史区间的表现。
3. 当波动率为 0 时它会失效。持有现金的夏普比率可能被算成无穷大,但显然不是好策略。
结论:夏普比率适合在相同条件下横向对比几种方案的性价比,但别把决策押在这一个数字上。本章表格里的数字只是简化夏普,更不能当作最终结论。怎么综合多个指标来判断一个策略到底好不好?这是第 5 章的主题。
---
## 3.6 本章总结
三次实验,三种权重分配方法,从“各买三分之一”到“按波动调整”再到“按涨势分配”,你一步步把“买多少”这个问题从拍脑袋变成了有据可依。买多少不是小问题。权重一变,每个资产对账户的影响就会变,整个策略的表现也会跟着变。
### 概念速查表
本章涉及的核心概念汇总如表 3-9 所示,方便随时回查。
**表 3-9 第 3 章核心概念速查**
| 概念 | 含义 | 类比 |
|------|------|------|
| 权重(Weight) | 每只资产分到总资金的百分之多少 | 一笔钱怎么分 |
| 权重分配方法 | 回答每只资产各买多少,在 open-xquant 里通常由 `PortfolioOptimizer` 实现 | 分蛋糕的方法 |
| 组合(Portfolio) | 实际拿来配置资金的一组资产,不是候选名单 | 购物车里真正放进去的商品 |
| 目标权重(Target Weight) | 按规则算出来的理想资金比例 | 预算表 |
| 持仓(Position) | 账户里实际持有的资产和数量 | 手里真正拿着的份额 |
| 实际权重 | 按持仓市值算出来的真实比例 | 结账后每样东西实际占了多少预算 |
| 风险平价(Risk Parity) | 按波动率倒数分配权重,让每只资产的影响更均匀 | 成熟稳重的多管钱,起伏大的少管钱 |
| 动量(Momentum) | 过去 N 天的涨跌趋势,正=涨势,负=跌势 | 最近的势头 |
| 动量排名(TopNRankingOptimizer | 按涨势强弱分配权重,跌的不买 | 趋势交易的量化表达 |
| 夏普比率(Sharpe Ratio) | 标准定义是(年化收益率 - 无风险收益率)/ 年化波动率;本章暂用简化夏普理解收益和波动的关系 | 每份颠簸赚多少 |
| 再平衡(Rebalancing) | 定期重新计算权重并调整持仓比例 | 定期重新分工 |
### 策略进化路径
第 2 章产出了标的池(沪深 300、纳指 100、黄金)。本章在此基础上探索了三种分钱方式。第 1 步,各买 1/3,也就是等权,简单,但波动大的资产会把组合拉着走。第 2 步,按波动调整,也就是风险平价,更稳,每只资产的影响更均匀。第 3 步,按涨势调整,也就是动量排名,这是趋势交易思想在“买多少”问题上的表达。第 3 章的产出是:三种权重分配方法对比。
### 本章最重要的收获
走完三个实验后,本章最值得带走的五条认知如表 3-10 所示。
**表 3-10 第 3 章主线认知**
| 认知 | 来源 |
|------|------|
| 平均分钱 ≠ 平均分影响力 | 3.1 |
| 波动率小 = 你能在低点不慌 | 3.1 |
| 按波动调整让组合更稳 | 3.2 |
| 同样的策略,只换权重分配方法,结果完全不同 | 3.3 |
| 简化夏普适合对比,但别只看一个指标 | 3.5 |
### 带走的问题
做完本章,你应该带着两个问题进入后续章节。它们将在下两章被一一回答:
- 权重分配方法只负责给出目标权重,但**什么时候调整持仓、什么条件下卖出?** → 第 4 章
- 动量排名在这段数据上全面胜出,但**换一段时间还行吗?怎么区分真本事和运气?** → 第 5 章
第二个问题正是本书口号“**做要规则、看要指标、还要怀疑你的指标**”中“疑”的入口。动量排名的累计 179.96% 看起来很漂亮,但你已经隐隐感到不对劲:换段时间还能不能这样?这种警觉,是后面几章要反复训练的主线。
> 本章所有代码的可运行版本见配套仓库的 [`q3-how-much/notebooks/q3-how-much.ipynb`](https://github.com/xingwudao/xquant-learning/blob/main/q3-how-much/notebooks/q3-how-much.ipynb)