# 动量因子对比调研报告

> 调研日期：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.07，ICIR=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（斜率×R²，归一化价格）

$$\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_slope（t-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（正负号）决定方向，不用收益率幅度
- 仓位大小由波动率倒数控制

**对负价格的态度**：简单收益天然兼容负价格，$(−5−60)/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`
