Files
etf/docs/动量因子对比调研报告.md
aszerW 921f84cb6a feat: 新增 standardized_slope (t-statistic) 因子并实验验证
- simple_rotation.py: 新增 standardized_slope_score 函数 (slope/SE)
- config_loader.py: FactorType 枚举新增 STANDARDIZED_SLOPE
- 对比实验结果: standardized_slope 年化 13.73% vs slope_r2 19.84%
- 结论: t-statistic 过度惩罚高波动资产的有效趋势信号,不适合本场景
- 文档更新: 动量因子对比调研报告新增 3.3 节详细分析
2026-06-06 16:40:01 +08:00

295 lines
11 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.

# 动量因子对比调研报告
> 调研日期2026-06-06
> 回测区间2020-01-10 ~ 2026-06-05
> 当前结论:`slope_r2` 作为默认因子(年化 19.84%,夏普 1.14
---
## 1. 问题背景
原始策略使用 `weighted_momentum` 因子,通过加权线性回归计算动量得分。诊断分析发现三个信号质量瓶颈:
1. **跨市场动量不可比**:同组换仓 alpha=+0.47%,跨组换仓仅 +0.03%
2. **IC 极低**IC=0.07ICIR=0.17
3. **T+1 执行噪声**:跨市场约 50% 翻转率
核心问题是:不同资产的动量得分量纲差异巨大(如 CL=F 原油极端值可达 375,527,068导致跨资产比较失真。
---
## 2. 四种因子公式对比
### 2.1 weighted_momentum加权线性回归动量
$$\text{Score} = (e^{\beta \times 250} - 1) \times R^2$$
**计算逻辑**
- 价格变换:$y = \ln(\text{prices})$,取对数
- 回归权重:$w = \text{linspace}(1, 2, n)$,近期权重线性递增
- 加权线性回归:$y = \beta x + \alpha$
- 年化收益:$e^{\beta \times 250} - 1$
- $R^2$:加权决定系数
**特点**:近期加权强调趋势延续性,对数收益使跨资产量纲部分可比。
---
### 2.2 vol_adjusted_momentum波动率调整动量
$$\text{Score} = \frac{\text{年化收益}}{\text{年化波动率}} \times R^2$$
**计算逻辑**
- 回归逻辑与 weighted_momentum 完全相同
- 额外除以已实现波动率:$\sigma = \text{std}(\text{daily\_returns}) \times \sqrt{250}$
- 最小波动率保护:$\sigma \geq 0.01$
**学术来源**Moskowitz, Ooi & Pedersen (2012) *Time Series Momentum*,类夏普比率构造。
**特点**:理论上使跨资产更可比,但实践中波动率计算对极端值敏感。
---
### 2.3 slope_r2斜率×归一化价格
$$\text{Score} = 10000 \times \text{slope} \times R^2$$
**计算逻辑**
- 价格变换:$y = \text{prices} / \text{prices}[0]$**归一化而非取对数**
- **无权重**,普通最小二乘回归:$y = \text{slope} \cdot x + \text{intercept}$
- $R^2$:无权重决定系数
- 乘以 10000 使数值范围便于阅读
**特点**:归一化天然消除量纲差异,所有资产起点为 1.0,斜率在同尺度上可比。
---
### 2.4 momentum简单收益率
$$\text{Score} = \frac{\text{prices}[-1]}{\text{prices}[0]} - 1$$
**计算逻辑**
- 最朴素:期末价格 / 期初价格 - 1
- 不考虑趋势质量(无 $R^2$)、不考虑波动率
**特点**:简单但缺乏趋势质量过滤,对短期噪声敏感。
---
## 3. 回测对比结果
### 3.1 核心指标
| 因子类型 | 年化收益 | 夏普比率 | 最大回撤 | Calmar | 调仓次数 | 胜率 |
|---------|---------|---------|---------|--------|---------|------|
| weighted_momentum | 18.36% | 1.02 | -16.36% | 1.12 | 405 | 54.0% |
| vol_adjusted_momentum | 13.16% | 0.85 | -18.61% | 0.71 | 393 | 55.9% |
| **slope_r2当前默认** | **19.84%** | **1.14** | **-15.35%** | **1.29** | 394 | 54.1% |
| momentum | 9.27% | 0.57 | -17.42% | 0.53 | 729 | 53.3% |
| standardized_slope | 13.73% | 1.01 | -13.52% | 1.02 | 335 | 54.5% |
**结论**`slope_r2` 全面胜出,年化 +1.48%,夏普 +0.12,回撤改善 +1.01%。
> **注**`standardized_slope`t-statistic回撤更小但收益大幅落后年化 -6.11%),说明统计显著性过滤在高波动资产上过度惩罚趋势信号,不适合本场景(详见 3.3)。
### 3.2 数值尺度分析2024-06-03 截面)
| 因子 | 最大值 | 最小正值 | max/min 比值 |
|------|-------|---------|-------------|
| weighted_momentum | 0.633 | 0.0003 | **2179x** |
| vol_adjusted_momentum | 4.812 | 0.0014 | **3378x** |
| **slope_r2** | **23.18** | **0.745** | **31x** |
| momentum | 0.046 | 0.0005 | 90x |
`slope_r2` 的跨资产数值差距仅 31 倍,远小于其他因子的 2000~3000 倍,这是其跨市场可比的根本原因。
### 3.3 standardized_slopet-statistic实验
**公式**
$$\text{Score} = \frac{\hat{\beta}}{\text{SE}(\hat{\beta})}, \quad \text{SE}(\hat{\beta}) = \sqrt{\frac{\text{MSE}}{S_{xx}}}$$
**学术动机**t-statistic 同时考虑了斜率大小和估计的统计显著性,理论上比 `slope × R²` 更严格。
**实验结果**
| 指标 | slope_r2 | standardized_slope | Δ |
|------|---------|-------------------|---|
| 年化收益 | 19.84% | 13.73% | **-6.11%** |
| 夏普比率 | 1.14 | 1.01 | **-0.12** |
| 最大回撤 | -15.35% | -13.52% | +1.83% |
| Calmar | 1.29 | 1.02 | -0.27 |
| 调仓次数 | 394 | 335 | -59 |
**失败原因分析**
- **绝对度量 vs 相对度量**`SE(β)` 是绝对度量(量纲同斜率),而 `R²` 是相对度量无量纲。在跨资产比较中SE 对高波动资产(如 CL=F、HSTECH惩罚过重即使趋势方向正确score 也会被压低。
- **过度过滤**:调仓次数减少 59 次,说明 t-statistic 把大量"方向对但波动大"的有效信号过滤掉了,反而错失趋势行情。
- **数学等价性**`slope / SE(slope) = slope × √(Sxx / MSE)`,而 `slope × R² = slope × (1 - SS_res/SS_tot)`。前者惩罚的是残差方差绝对值,后者惩罚的是偏离趋势线的比例——后者更适合作为趋势质量因子。
**结论**t-statistic 不适合本场景,保持 `slope_r2` 为默认因子。
---
## 4. slope_r2 胜出的原因分析
### 4.1 跨市场可比性:归一化消除量纲
| 设计选择 | weighted 的做法 | slope_r2 的做法 | 为什么 slope_r2 更好 |
|---------|---------------|----------------|-------------------|
| 价格变换 | $\ln(p)$ | $p/p_0$ | 归一化后所有资产同尺度 |
| 回归权重 | 近期加权(1→2) | 无权重 | 25 天窗口已短,等权更抗噪 |
| $R^2$ 质量因子 | ✓ | ✓ | 都保留了趋势质量过滤 |
### 4.2 不加权 > 加权:降噪效应
25 天窗口本身就不长,加权(权重 1→2相当于把有效窗口缩到约 17 天,增加了随机性。等权回归对 25 天趋势的估计更稳健。
### 4.3 负价格免疫力
WTI 原油 2020-04-21 出现 -37.63 美元/桶的负价格。各因子处理方式:
| 因子 | 处理方式 | 问题 |
|------|---------|------|
| weighted | $\ln(\text{clip}(0.01)) = -4.6$ | 虚假平台拉偏回归 |
| vol_adjusted | 对数收益率跳跃 | vol 被放大score 压低 |
| **slope_r2** | $0.01/60 \approx 0.000167$ | 跌幅压缩但不爆炸 |
| momentum | $25/0.01 - 1 = 2499$ | 荒谬收益率,严重误判 |
---
## 5. 业界学界方法调研
### 5.1 Moskowitz, Ooi & Pedersen (2012) — TSMOM
**核心公式**
```
Signal = sign(r_{t-12, t}) // 仅取过去12月超额收益的符号
Position = (1/σ_t) × Signal // 仓位反比于波动率
```
**关键设计**
- 使用**期货超额收益**(简单收益),不用对数收益
- 只用 sign正负号决定方向不用收益率幅度
- 仓位大小由波动率倒数控制
**对负价格的态度**:简单收益天然兼容负价格,$(560)/60 = 108\%$ 合法。
### 5.2 Baltas & Kosowski (2012) — 线性趋势拟合
> "Momentum trading signals generated by **fitting a linear trend on the asset price path** maximise the out-of-sample performance while minimizing the portfolio turnover."
这正是 `slope_r2` 的思路——直接拟合价格路径而非计算收益率。
**结论**:线性趋势拟合 > 简单收益率 > 加权收益率。
### 5.3 Dudler, Gmuer, Malamud (2014) — Risk-Adjusted Momentum
$$\text{RAMOM} = \text{mean}\left(\frac{r_1}{\sigma_1}, \frac{r_2}{\sigma_2}, \ldots, \frac{r_n}{\sigma_n}\right)$$
每天先算风险调整收益 $r_t/\sigma_t$,再取均值作为信号。比 TSMOM 整体更优。
### 5.4 AQR / Man Group / Winton — 实盘 CTA 做法
| 方法 | 做法 | 代表机构 |
|------|------|---------|
| **简单收益替代对数** | $r = (P_t - P_{t-1}) / P_{t-1}$ | AQR, Man AHL |
| **排除/跳过** | 窗口内出现非正价格时信号设为 0 | 多数 CTA |
| **连续合约** | 使用 roll-adjusted futures 价格 | 所有正规 CTA |
| **波动率缩放** | 只用 sign + vol 倒数,不用幅度 | Moskowitz 方案 |
---
## 6. 负价格处理机制分析
### 6.1 当前实现的问题
四个因子均使用 `np.clip(prices, 0.01, None)` 处理负价格:
```python
prices = np.clip(prices, 0.01, None) # 负值 → 0.01
```
**问题**:这是粗暴的掩盖而非正确处理。
| 因子 | clip 后的核心问题 | 严重程度 |
|------|------------------|---------|
| weighted | $\log(0.01) = -4.6$ 形成假平台,拉偏回归 | 中 |
| vol_adjusted | 对数收益率剧烈跳跃vol 被放大 | **高** |
| slope_r2 | 跌幅被压缩,但不产生数值爆炸 | **低** |
| momentum | 首尾任一被 clip 就产生荒谬比值 | **极高** |
### 6.2 推荐改进方案
**方案 A排除法推荐**
窗口内出现非正价格 → 返回 `None`,该资产不参与排名。
```python
def _compute_momentum(self, signal_code: str, date: pd.Timestamp) -> Optional[float]:
# ... 获取 prices ...
if np.any(prices <= 0):
return None # 负价格期间不参与交易
# ... 计算 score ...
```
**优点**
- 负油价在历史上只出现几天,排除影响极小
- 避免所有 clip 导致的信号扭曲
- 实现成本极低
**方案 B简单收益替代对数**
`weighted_momentum``vol_adjusted``log(prices)` 改为简单收益率。但 `slope_r2` 已在价格空间操作,无需修改。
**方案 C保持现状**
`slope_r2` 已是当前最优(年化 19.84%且对负价格抵抗力最强。clip 只在极端情况触发,实际影响有限。
---
## 7. 结论与建议
### 7.1 当前决策
**采用 `slope_r2` 作为默认因子**,配置已切换至 `config_simple.yaml`
```yaml
factor:
type: slope_r2
n_days: 25
```
### 7.2 理论依据
1. **跨市场可比**:归一化价格天然消除量纲差异
2. **趋势质量**$R^2$ 过滤噪声趋势
3. **抗极端值**:不使用对数,对负价格免疫力最强
4. **学术支持**Baltas & Kosowski (2012) 证实线性趋势拟合优于简单收益率
### 7.3 后续优化方向
| 方向 | 描述 | 优先级 |
|------|------|-------|
| 负价格排除 | 窗口内出现非正价格时返回 None | 低(实际影响极小) |
| 多窗口融合 | 结合 5/25/60 天信号 | 中 |
| 截面 rank | 动量值转截面百分位排名 | 低slope_r2 已天然可比) |
| ~~标准化斜率~~ | slope/SE(slope),已验证不适合(详见 3.3 | **已排除** |
---
## 附录:实验代码
对比实验脚本:`rotation/experiments/factor_comparison.py``rotation/experiments/std_slope_test.py`
运行方式:
```bash
cd /Users/aszer/code/etf
set -a && source .env && set +a
python rotation/experiments/factor_comparison.py
python rotation/experiments/std_slope_test.py
```
结果输出:
- `rotation/experiments/output/factor_comparison_results.json`
- `rotation/experiments/output/std_slope_test_results.json`