471 lines
16 KiB
Markdown
471 lines
16 KiB
Markdown
# 实验 007:动量因子回看窗口优化研究
|
||
|
||
**实验日期**:2026-06-12
|
||
**实验目标**:研究动量因子回看窗口(n_days)的选择方法,评估多周期融合对策略表现的影响
|
||
**实验结论**:多周期融合不适用于本策略,维持 25 天单窗口
|
||
|
||
---
|
||
|
||
## 一、问题背景
|
||
|
||
### 1.1 当前配置
|
||
|
||
策略使用 `slope_r2` 因子,全局统一 25 天回看窗口:
|
||
|
||
```yaml
|
||
factor:
|
||
n_days: 25
|
||
type: slope_r2
|
||
```
|
||
|
||
### 1.2 发现的问题
|
||
|
||
从回测数据的因子分布分析发现:
|
||
|
||
| 标的 | IQR(波动代理) | 中位数 | 正得分% | 特征 |
|
||
|------|----------------|--------|---------|------|
|
||
| 创业板指 | 28.23 | 0.00 | 51.0% | 高波动,趋势间歇性强 |
|
||
| 恒生科技 | 20.71 | -0.01 | 44.0% | 高波动,趋势弱 |
|
||
| 纳指100 | 20.10 | 4.31 | 67.4% | 高波动,趋势强 |
|
||
| 短债 | 0.38 | 0.68 | 97.9% | 低波动,几乎无趋势 |
|
||
| 黄金 | 13.39 | 0.77 | 60.7% | 中等波动,趋势持续 |
|
||
|
||
**核心问题**:不同资产的趋势周期差异很大,统一 25 天窗口是否合理?
|
||
|
||
---
|
||
|
||
## 二、学界与业界调研
|
||
|
||
### 2.1 经典文献的标准做法
|
||
|
||
#### 横截面动量(Cross-Sectional Momentum)
|
||
|
||
**Jegadeesh & Titman (1993)** 开创性研究:
|
||
- **标准窗口**:12-1 月(过去12个月收益,跳过最近1个月)
|
||
- **金融语义**:跳过最近1个月是因为存在短期反转效应(1周-1个月),而中期(3-12个月)存在动量效应
|
||
- **原理**:信息扩散慢 → 价格对新信息反应不足 → 形成中期趋势
|
||
|
||
**Asness, Moskowitz & Pedersen (2013)** "Value and Momentum Everywhere":
|
||
- 股票:12-1 月
|
||
- 债券:12-1 月或 6-1 月
|
||
- 商品:12 月(不跳过,因为商品市场短期反转弱)
|
||
- 货币:12 月
|
||
- **核心观点**:不同资产类别的最优窗口不同,但 12 月是一个稳健的起点
|
||
|
||
#### 时间序列动量(Time-Series Momentum / TSMOM)
|
||
|
||
**Moskowitz, Ooi & Pedersen (2012)**:
|
||
- **标准窗口**:12 月(用于期货)
|
||
- **金融语义**:TSMOM 关注资产自身的绝对收益,不与其他资产比较
|
||
- **关键发现**:1-12 月窗口都有效,但 12 月最稳健
|
||
- **波动率调整**:用实现波动率标准化头寸规模,使得不同资产可比
|
||
|
||
### 2.2 窗口选择的金融语义
|
||
|
||
#### 不同窗口对应的市场微观结构
|
||
|
||
| 窗口长度 | 捕捉的效应 | 金融解释 | 风险 |
|
||
|---------|-----------|---------|------|
|
||
| 1周-1月 | 短期反转 | 流动性冲击、过度反应修正 | 高换手、交易成本 |
|
||
| 1-3月 | 早期动量 | 信息扩散初期、盈余公告后漂移 | 容易被打断 |
|
||
| 3-12月 | 经典动量 | 信息扩散慢、机构调仓周期 | 最稳健 |
|
||
| 12-24月 | 长期动量 | 经济周期、企业基本面变化 | 均值回归开始显现 |
|
||
| >24月 | 长期反转 | 估值回归、经济周期反转 | 动量效应消失 |
|
||
|
||
#### 不同资产类别的特征周期
|
||
|
||
| 资产类别 | 特征周期 | 推荐窗口 | 理由 |
|
||
|---------|---------|---------|------|
|
||
| **股票指数** | 季度财报+机构调仓 | 6-12月 | 信息扩散慢,机构季度调仓 |
|
||
| **债券** | 央行政策周期 | 3-6月 | 利率变化快,久期短 |
|
||
| **商品** | 供需周期+季节性 | 6-12月 | 供需调整慢,但有季节性 |
|
||
| **货币** | 利差+央行政策 | 3-6月 | 政策变化快 |
|
||
|
||
### 2.3 避免过拟合的原则
|
||
|
||
#### 先验选择 vs 数据挖掘
|
||
|
||
**过拟合的做法**:
|
||
```python
|
||
# 在历史数据上测试 5, 10, 15, 20, 25, 30... 天,选最好的
|
||
for window in [5, 10, 15, 20, 25, 30, 60, 120]:
|
||
backtest(window)
|
||
best_window = argmax(results) # 过拟合!
|
||
```
|
||
|
||
**有金融语义的做法**:
|
||
```python
|
||
# 基于资产类别选择窗口
|
||
window_map = {
|
||
'equity_index': 252, # 12月(252交易日)
|
||
'bond': 126, # 6月
|
||
'commodity': 252, # 12月
|
||
'currency': 126, # 6月
|
||
}
|
||
```
|
||
|
||
#### 稳健性检验原则
|
||
|
||
**学界推荐的做法**:
|
||
1. **选择有理论支撑的窗口**:12月、6月、3月是标准选择
|
||
2. **测试邻域稳健性**:如果 12 月好,11 月和 13 月也应该不差
|
||
3. **多窗口平均**:用 3-12 月的多个窗口取平均,降低单窗口风险
|
||
4. **样本外验证**:在不同时间段、不同市场验证
|
||
|
||
**AQR 的实践建议**:
|
||
- 不要优化到极端值(如 17 天、23 天)
|
||
- 选择"足够好"的标准窗口(如 252 天而非 247 天)
|
||
- 关注经济解释而非统计显著性
|
||
|
||
### 2.4 多窗口融合方法
|
||
|
||
#### 1. 等权平均(Simple Ensemble)
|
||
|
||
```python
|
||
windows = [63, 126, 252] # 3月、6月、12月
|
||
momentum = mean([return(p, w) for w in windows])
|
||
```
|
||
|
||
**优点**:
|
||
- 降低单窗口风险
|
||
- 捕捉不同周期的趋势
|
||
- 无需优化参数
|
||
|
||
**缺点**:
|
||
- 等权可能不合理
|
||
- 可能引入噪音窗口
|
||
|
||
#### 2. 波动率加权(Volatility-Weighted)
|
||
|
||
```python
|
||
# 波动率低的窗口权重更高(更稳定)
|
||
weights = 1 / vol(window_i)
|
||
momentum = weighted_mean(momentum_i, weights)
|
||
```
|
||
|
||
**金融语义**:低波动窗口的信号更可靠
|
||
|
||
#### 3. 自适应窗口(Adaptive Window)
|
||
|
||
**基于波动率的自适应**:
|
||
```python
|
||
# 高波动时缩短窗口(快速反应),低波动时延长(过滤噪音)
|
||
if realized_vol > threshold:
|
||
window = 63 # 3月
|
||
else:
|
||
window = 252 # 12月
|
||
```
|
||
|
||
**基于机制转换(Regime Switching)**:
|
||
```python
|
||
# 用 HMM 识别市场状态
|
||
regime = detect_regime(market_data)
|
||
if regime == 'trending':
|
||
window = 252 # 趋势市用长窗口
|
||
elif regime == 'mean_reverting':
|
||
window = 21 # 震荡市用短窗口或反转
|
||
```
|
||
|
||
### 2.5 多周期融合为什么有效?
|
||
|
||
#### 1. 信息扩散有多个时间尺度
|
||
|
||
- **短期(1-4周)**:流动性冲击、技术性买卖、短期情绪(噪音)
|
||
- **中期(1-6月)**:盈余公告、行业数据、政策变化(信息扩散)
|
||
- **长期(6-12月)**:经济周期转换、产业趋势、估值重定价
|
||
|
||
单一窗口只能捕捉一个尺度的信息。多窗口融合等于同时监听多个信息频段。
|
||
|
||
#### 2. 市场参与者的决策周期不同
|
||
|
||
| 参与者 | 决策周期 | 影响的价格趋势 |
|
||
|--------|---------|--------------|
|
||
| 高频/量化 | 天-周 | 短期噪音 |
|
||
| 共同基金 | 月-季 | 中期动量 |
|
||
| 养老金/保险 | 季-年 | 长期趋势 |
|
||
| 主权基金 | 年+ | 结构性变化 |
|
||
|
||
当多个周期的信号一致时,意味着不同时间维度的市场参与者方向一致——这是最强的趋势确认。
|
||
|
||
#### 3. 统计角度:偏差-方差权衡
|
||
|
||
- **短窗口**:低偏差(快速捕捉趋势变化),高方差(容易被噪音干扰)
|
||
- **长窗口**:高偏差(趋势转折时反应慢),低方差(噪音被平滑掉)
|
||
|
||
多窗口融合相当于对偏差-方差做了平均,短窗口提供灵敏度,长窗口提供稳定性。
|
||
|
||
#### 4. 信号处理的类比
|
||
|
||
把价格序列想象成一个信号,不同窗口就是不同频率的带通滤波器:
|
||
|
||
```
|
||
价格信号 = 高频噪音 + 中期趋势 + 长期趋势 + 周期性波动
|
||
|
||
25天窗口 → 带通滤波器:主要透过高频成分
|
||
126天窗口 → 带通滤波器:主要透过中期成分
|
||
252天窗口 → 低通滤波器:只保留长期趋势
|
||
```
|
||
|
||
多个滤波器融合 = 宽频带接收,信息更完整。
|
||
|
||
### 2.6 多周期融合对 slope_r2 偏好的影响
|
||
|
||
slope_r2 真正偏好的是**趋势性波动高的资产**(高波动+有方向),而不是单纯的高波动。
|
||
|
||
多周期融合的预期影响:
|
||
|
||
| 资产类型 | 单窗口(25天) | 多周期融合 | 变化方向 |
|
||
|---------|-------------|-----------|---------|
|
||
| 高波动+持续趋势(纳指) | 高分 | 高分 | 不变 |
|
||
| 高波动+间歇趋势(创业板) | 不稳定高分 | 中等分 | **下降** |
|
||
| 低波动+持续趋势(黄金) | 中等分 | 中等偏高分 | **上升** |
|
||
| 低波动+无趋势(短债) | 低分 | 低分 | 不变 |
|
||
| 高波动+无趋势(恒生科技) | 低分 | 低分 | 不变 |
|
||
|
||
**核心预期**:融合会让 slope_r2 的偏好从"高波动+趋势性"转向"持续性趋势"——不管波动高低,只要趋势持续就得分高。
|
||
|
||
---
|
||
|
||
## 三、实验设计
|
||
|
||
### 3.1 实验一:IDM 信息离散动量融合
|
||
|
||
#### 方法
|
||
|
||
**IDM(Information Dispersal Momentum)**:正收益天数占比,衡量上涨的持续性
|
||
|
||
```python
|
||
def info_dispersal_momentum(prices: np.ndarray) -> float:
|
||
returns = np.diff(prices)
|
||
positive_days = np.sum(returns > 0)
|
||
return positive_days / len(returns)
|
||
```
|
||
|
||
**方式一:乘法融合**
|
||
```python
|
||
def slope_r2_idm_score(prices: np.ndarray) -> float:
|
||
sr2 = slope_r2_score(prices)
|
||
idm = info_dispersal_momentum(prices)
|
||
return sr2 * idm # IDM 作为折扣系数
|
||
```
|
||
|
||
**方式三:阈值过滤**
|
||
```python
|
||
def slope_r2_idm_filter_score(prices: np.ndarray, threshold: float = 0.5) -> float:
|
||
idm = info_dispersal_momentum(prices)
|
||
if idm < threshold:
|
||
return 0.0 # 上涨天数不足阈值则清零
|
||
return slope_r2_score(prices)
|
||
```
|
||
|
||
#### 实验配置
|
||
|
||
- **方式一**:`type: slope_r2_idm`
|
||
- **方式三**:`type: slope_r2_idm_filter`,测试阈值 0.4/0.5/0.6
|
||
|
||
### 3.2 实验二:多周期融合
|
||
|
||
#### 方法
|
||
|
||
```python
|
||
def slope_r2_ensemble_score(prices: np.ndarray, windows: list = None) -> float:
|
||
if windows is None:
|
||
windows = [63, 126, 252] # 3月、6月、12月
|
||
|
||
scores = []
|
||
for w in windows:
|
||
if len(prices) >= w:
|
||
window_prices = prices[-w:]
|
||
score = slope_r2_score(window_prices)
|
||
scores.append(score)
|
||
|
||
return sum(scores) / len(scores) if scores else 0.0
|
||
```
|
||
|
||
#### 实验配置
|
||
|
||
- **配置**:`type: slope_r2_ensemble`
|
||
- **窗口**:63/126/252 天(3月/6月/12月)
|
||
- **数据预加载**:504 天(2倍最大窗口)
|
||
|
||
---
|
||
|
||
## 四、实验结果
|
||
|
||
### 4.1 实验一:IDM 融合结果
|
||
|
||
#### 方式一:乘法融合
|
||
|
||
| 指标 | slope_r2 (baseline) | slope_r2_idm | 变化 |
|
||
|------|---------------------|--------------|------|
|
||
| 总收益 | 288.30% | 296.55% | +8.25% |
|
||
| 年化收益 | 24.61% | 25.03% | +0.42% |
|
||
| 最大回撤 | -16.27% | -16.19% | 略改善 |
|
||
| Sharpe | 1.17 | 1.20 | +0.03 |
|
||
| Calmar | 1.51 | 1.55 | +0.04 |
|
||
| 胜率 | 53.74% | 54.48% | +0.74% |
|
||
| 调仓次数 | 363 | 374 | +11 |
|
||
|
||
**结论**:方式一(乘法融合)全面小幅优于 baseline。
|
||
|
||
#### 方式三:阈值过滤
|
||
|
||
| 阈值 | 总收益 | 年化 | 最大回撤 | Sharpe | Calmar | 胜率 |
|
||
|------|--------|------|----------|--------|--------|------|
|
||
| 0.4 | 297.88% | 25.10% | -16.27% | 1.19 | 1.54 | 53.87% |
|
||
| 0.5 | 205.38% | 19.85% | -17.90% | 1.00 | 1.11 | 53.35% |
|
||
| 0.6 | 69.75% | 8.96% | -24.77% | 0.58 | 0.36 | 56.87% |
|
||
|
||
**结论**:方式三(过滤器)阈值敏感,0.5 和 0.6 明显变差,容易过拟合。
|
||
|
||
### 4.2 实验二:多周期融合结果
|
||
|
||
#### 绩效对比
|
||
|
||
| 指标 | slope_r2 (25天) | slope_r2_ensemble (63/126/252) | 变化 |
|
||
|------|----------------|-------------------------------|------|
|
||
| 总收益 | 288.30% | 182.82% | **-105.48%** |
|
||
| 年化收益 | 24.61% | 18.36% | **-6.25%** |
|
||
| 最大回撤 | -16.27% | -21.61% | **恶化 5.34%** |
|
||
| Sharpe | 1.17 | 0.96 | **-0.21** |
|
||
| Calmar | 1.51 | 0.85 | **-0.66** |
|
||
| 胜率 | 53.74% | 55.47% | +1.73% |
|
||
| 调仓次数 | 363 | 167 | **-196** |
|
||
|
||
#### 持仓频率变化
|
||
|
||
| 标的 | baseline 占比 | ensemble 占比 | 变化 | 资产类型 |
|
||
|------|--------------|--------------|------|---------|
|
||
| 纳指100 | 44.7% | 53.3% | **+8.6%** | 高波动+持续趋势 |
|
||
| 黄金 | 21.0% | 35.8% | **+14.8%** | 低波动+持续趋势 |
|
||
| 创业板指 | 29.9% | 40.2% | **+10.3%** | 高波动+间歇趋势 |
|
||
| 日经225 | 31.0% | 37.7% | +6.7% | 中波动+持续趋势 |
|
||
| 德国DAX | 27.8% | 33.1% | +5.3% | 中波动+持续趋势 |
|
||
| **短债指数** | **32.0%** | **16.6%** | **-15.4%** | 防御填充 |
|
||
| 红利低波 | 24.3% | 11.9% | **-12.4%** | 低波动+持续趋势 |
|
||
| 有色金属 | 18.1% | 12.8% | -5.3% | 高波动+周期趋势 |
|
||
|
||
#### 与预期对比
|
||
|
||
| 预期 | 实际 | 符合? |
|
||
|------|------|--------|
|
||
| 纳指100 和黄金占比上升 | 纳指+8.6%,黄金+14.8% | ✓ 符合 |
|
||
| 红利低波占比上升 | 红利低波-12.4% | ✗ 不符合 |
|
||
| 创业板指占比下降 | 创业板指+10.3% | ✗ 不符合 |
|
||
| 整体表现改善 | 收益降6%,回撤增5% | ✗ 不符合 |
|
||
|
||
---
|
||
|
||
## 五、结论与分析
|
||
|
||
### 5.1 IDM 融合结论
|
||
|
||
**推荐方案**:方式一(乘法融合)
|
||
|
||
**理由**:
|
||
1. 全面小幅优于 baseline,无需调参
|
||
2. IDM 作为折扣系数,逻辑简洁
|
||
3. 过滤方式(方式三)阈值敏感,容易过拟合
|
||
|
||
**保留代码**:
|
||
- `slope_r2_idm_score` 函数已实现
|
||
- `FactorType.SLOPE_R2_IDM` 枚举已添加
|
||
- 可通过配置 `type: slope_r2_idm` 启用
|
||
|
||
### 5.2 多周期融合结论
|
||
|
||
**结论**:不适用于本策略
|
||
|
||
**原因分析**:
|
||
|
||
1. **长窗口反应太慢**:252 天窗口在趋势转折时严重滞后。2022 年全球熊市、2024 年风格切换时,ensemble 无法及时退出
|
||
|
||
2. **调仓次数骤降**:从 363 次降到 167 次,说明信号太稳定了,错过了很多轮动机会。这个策略的核心价值就是**轮动**,过于稳定的信号反而不利
|
||
|
||
3. **短债填充减少**:从 32% 降到 16.6%,说明 ensemble 在弱势市场中也倾向于持有风险资产(因为长窗口记忆了之前的上涨趋势),导致回撤增大
|
||
|
||
4. **创业板指上升的原因**:2024-2025 年创业板有持续上涨趋势,ensemble 的长窗口恰好捕捉到了这个趋势,但这不是"持续性偏好",而是"恰好匹配"
|
||
|
||
**核心问题**:
|
||
|
||
这个策略的 alpha 来源是**中短期轮动**(25 天窗口),不是长期趋势跟踪。ensemble 把因子变成了半趋势跟踪因子,与策略的核心逻辑冲突。
|
||
|
||
### 5.3 最终决策
|
||
|
||
**维持现有配置**:
|
||
```yaml
|
||
factor:
|
||
n_days: 25
|
||
type: slope_r2
|
||
```
|
||
|
||
**理由**:
|
||
1. 25 天窗口与策略的轮动逻辑匹配
|
||
2. 多周期融合与策略核心逻辑冲突
|
||
3. IDM 融合虽然有效,但提升有限,暂不启用
|
||
|
||
---
|
||
|
||
## 六、参考资料
|
||
|
||
### 学术文献
|
||
|
||
1. **Jegadeesh, N., & Titman, S. (1993)**. Returns to Buying Winners and Selling Losers: Implications for Stock Market Efficiency. *Journal of Finance*, 48(1), 65-91.
|
||
|
||
2. **Asness, C. S., Moskowitz, T. J., & Pedersen, L. H. (2013)**. Value and Momentum Everywhere. *Journal of Finance*, 68(3), 929-985.
|
||
|
||
3. **Moskowitz, T. J., Ooi, Y. H., & Pedersen, L. H. (2012)**. Time Series Momentum. *Journal of Financial Economics*, 104(2), 228-250.
|
||
|
||
### 业界实践
|
||
|
||
- [Momentum Factor Investing: 30 years of Out of Sample Data](https://alphaarchitect.com/momentum-factor-investing-30-years-of-out-of-sample-data/)
|
||
- [Systematic Trend-Following with Adaptive Portfolio Construction](https://arxiv.org/html/2602.11708v1)
|
||
- [Value and Momentum Everywhere - AQR Capital Management](https://www.aqr.com/Insights/Research/Journal-Article/Value-and-Momentum-Everywhere)
|
||
|
||
---
|
||
|
||
## 七、附录:代码实现
|
||
|
||
### 7.1 IDM 融合因子
|
||
|
||
```python
|
||
def info_dispersal_momentum(prices: np.ndarray) -> float:
|
||
"""Information Dispersal Momentum (IDM): 正收益天数占比"""
|
||
if len(prices) < 2:
|
||
return 0.0
|
||
returns = np.diff(prices)
|
||
positive_days = np.sum(returns > 0)
|
||
return positive_days / len(returns)
|
||
|
||
|
||
def slope_r2_idm_score(prices: np.ndarray) -> float:
|
||
"""Slope * R² * IDM: 趋势强度 × 拟合质量 × 上涨持续性"""
|
||
sr2 = slope_r2_score(prices)
|
||
idm = info_dispersal_momentum(prices)
|
||
return sr2 * idm
|
||
```
|
||
|
||
### 7.2 多周期融合因子
|
||
|
||
```python
|
||
def slope_r2_ensemble_score(prices: np.ndarray, windows: list = None) -> float:
|
||
"""多窗口 slope_r2 等权融合"""
|
||
if windows is None:
|
||
windows = [63, 126, 252] # 3月、6月、12月
|
||
|
||
scores = []
|
||
for w in windows:
|
||
if len(prices) >= w:
|
||
window_prices = prices[-w:]
|
||
score = slope_r2_score(window_prices)
|
||
scores.append(score)
|
||
|
||
return sum(scores) / len(scores) if scores else 0.0
|
||
```
|
||
|
||
---
|
||
|
||
**文档版本**:v1.0
|
||
**最后更新**:2026-06-12
|
||
**实验状态**:已完成
|