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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user