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 节详细分析
This commit is contained in:
2026-06-06 16:40:01 +08:00
parent aff04318b1
commit 921f84cb6a
5 changed files with 224 additions and 2 deletions

View File

@@ -123,6 +123,39 @@ def slope_r2_score(prices: np.ndarray) -> float:
return 10000 * slope * r2
def standardized_slope_score(prices: np.ndarray) -> float:
"""Standardized slope (t-statistic): slope / SE(slope)
Academic basis:
- Uses normalized prices (p/p[0]) for cross-asset comparability,
consistent with slope_r2_score.
- Divides slope by its standard error, yielding a statistical significance
measure rather than raw magnitude.
- Equivalent to the t-value for H0: slope=0, penalizing noisy trends.
Formula:
SE(slope) = sqrt(MSE / Sxx)
MSE = SS_res / (n - 2)
Sxx = sum((xi - x_bar)^2) = n*(n-1)*(n+1)/12 for x = 0..n-1
"""
n = len(prices)
if n < 5:
return 0.0
prices = np.clip(prices, 0.01, None)
y = prices / prices[0] # normalize
x = np.arange(n)
slope, intercept = np.polyfit(x, y, 1)
y_pred = slope * x + intercept
ss_res = np.sum((y - y_pred) ** 2)
# Standard error of slope
mse = ss_res / (n - 2) # unbiased MSE
sxx = n * (n - 1) * (n + 1) / 12 # sum of squared deviations of x
se_slope = math.sqrt(mse / sxx) if sxx > 0 else 1e-9
if se_slope < 1e-12:
se_slope = 1e-12
return slope / se_slope
def momentum_score(prices: np.ndarray) -> float:
"""Simple price return: (last / first) - 1"""
if len(prices) < 5:
@@ -409,6 +442,8 @@ class SimpleRotationStrategy:
return vol_adjusted_momentum_score(prices)
elif ft == FactorType.SLOPE_R2:
return slope_r2_score(prices)
elif ft == FactorType.STANDARDIZED_SLOPE:
return standardized_slope_score(prices)
elif ft == FactorType.MOMENTUM:
return momentum_score(prices)
return weighted_momentum_score(prices)