add valuation, market regime and risk factors with adjusted factor limits

This commit is contained in:
sam 2025-10-08 18:52:07 +08:00
parent 34f0758135
commit 574a7c2682
3 changed files with 134 additions and 7 deletions

View File

@ -116,6 +116,18 @@ EXTENDED_FACTORS: List[FactorSpec] = [
# 成交量均线比率因子 # 成交量均线比率因子
FactorSpec("volume_ma_5_ratio", 5), # 当前成交量与5日均线比率 FactorSpec("volume_ma_5_ratio", 5), # 当前成交量与5日均线比率
FactorSpec("volume_ma_20_ratio", 20), # 当前成交量与20日均线比率 FactorSpec("volume_ma_20_ratio", 20), # 当前成交量与20日均线比率
# 估值因子
FactorSpec("val_ps_score", 0), # PS估值分数
FactorSpec("val_multiscore", 0), # 多维估值分数
FactorSpec("val_dividend_score", 0), # 股息评分
# 市场状态因子
FactorSpec("market_regime", 20), # 市场状态
FactorSpec("trend_strength", 20), # 趋势强度
# 风险因子
FactorSpec("risk_penalty", 20), # 风险惩罚因子
] ]
@ -337,6 +349,121 @@ class ExtendedFactors:
ma = rolling_mean(volume_series, window) ma = rolling_mean(volume_series, window)
return volume_series[0] / ma if ma > 0 else None return volume_series[0] / ma if ma > 0 else None
# 估值因子
elif factor_name == "val_ps_score":
# PS估值分数基于PS比率的估值指标
# 假设PS比率越低估值越有吸引力
if len(close_series) < 10:
return None
# 简化的PS估值基于价格与历史均值的比较
current_price = close_series[0]
avg_price = np.mean(close_series[:10])
if avg_price > 0:
# 当前价格相对于历史均值的偏离程度
ps_ratio = current_price / avg_price
# 标准化到[-1, 1]区间
return np.clip((1.0 - ps_ratio) / 2.0, -1, 1)
return None
elif factor_name == "val_multiscore":
# 多维估值分数:综合多个估值维度的评分
if len(close_series) < 20:
return None
# 使用价格动量、波动率和相对强度作为估值代理
momentum_5 = momentum(close_series, 5)
momentum_20 = momentum(close_series, 20)
# 计算波动率(手动实现,避免依赖外部函数)
if len(close_series) >= 20:
returns = [close_series[i] / close_series[i+1] - 1 for i in range(19)] # 修正索引范围
volatility_20 = np.std(returns) if returns else 0
else:
volatility_20 = 0
# 综合评分:动量正向,波动率负向
if volatility_20 > 0:
score = (momentum_5 + momentum_20) / (2 * volatility_20)
return np.clip(score, -1, 1)
return None
elif factor_name == "val_dividend_score":
# 股息评分:基于价格稳定性和趋势的股息吸引力评分
if len(close_series) < 20:
return None
# 计算价格稳定性(低波动率)
if len(close_series) >= 20:
returns = [close_series[i] / close_series[i+1] - 1 for i in range(19)] # 修正索引范围
vol = np.std(returns) if returns else 0
else:
vol = 0
# 计算趋势强度
trend = momentum(close_series, 20)
# 股息吸引力:稳定性正向,趋势正向
stability_score = 1.0 - np.clip(vol, 0, 1)
trend_score = np.clip(trend, -1, 1)
return (stability_score + trend_score) / 2.0
# 市场状态因子
elif factor_name == "market_regime":
# 市场状态:基于价格和成交量的市场状态判断
if len(close_series) < 20 or len(volume_series) < 20:
return None
# 价格趋势
price_trend = momentum(close_series, 20)
# 成交量趋势
volume_trend = momentum(volume_series, 20)
# 市场状态:牛市(价格↑成交量↑)、熊市(价格↓成交量↓)、
# 震荡市(价格平稳成交量平稳)、背离市(价格成交量反向)
regime_score = price_trend * volume_trend
return np.clip(regime_score, -1, 1)
elif factor_name == "trend_strength":
# 趋势强度:基于价格变动的趋势强度度量
if len(close_series) < 20:
return None
# 计算不同时间窗口的动量
momentum_5 = momentum(close_series, 5)
momentum_10 = momentum(close_series, 10)
momentum_20 = momentum(close_series, 20)
# 趋势强度:短期、中期、长期动量的一致性
trend_strength = (momentum_5 + momentum_10 + momentum_20) / 3.0
return np.clip(trend_strength, -1, 1)
# 风险因子
elif factor_name == "risk_penalty":
# 风险惩罚因子:基于波动率和异常价格的综合风险度量
if len(close_series) < 20:
return None
# 波动率风险
if len(close_series) >= 20:
returns = [close_series[i] / close_series[i+1] - 1 for i in range(19)] # 修正索引范围
vol_risk = np.std(returns) if returns else 0
else:
vol_risk = 0
# 价格异常风险(相对于均值的偏离)
avg_price = np.mean(close_series[:20])
if avg_price > 0:
price_deviation = abs(close_series[0] / avg_price - 1.0)
else:
price_deviation = 0
# 综合风险评分
risk_score = (vol_risk + price_deviation) / 2.0
return np.clip(risk_score, 0, 1) # 风险因子范围为[0, 1]
raise ValueError(f"因子 {factor_name} 没有对应的计算实现") raise ValueError(f"因子 {factor_name} 没有对应的计算实现")
def compute_all_factors(self, def compute_all_factors(self,

View File

@ -585,7 +585,7 @@ def _compute_security_factors(
volume_ratio = latest_fields.get("daily_basic.volume_ratio") volume_ratio = latest_fields.get("daily_basic.volume_ratio")
results[spec.name] = _volume_ratio_score(volume_ratio) results[spec.name] = _volume_ratio_score(volume_ratio)
else: else:
LOGGER.debug( LOGGER.info(
"忽略未识别的因子 name=%s ts_code=%s", "忽略未识别的因子 name=%s ts_code=%s",
spec.name, spec.name,
ts_code, ts_code,

View File

@ -14,18 +14,18 @@ FACTOR_LIMITS = {
"mom_": (-1.0, 1.0), "mom_": (-1.0, 1.0),
# 波动率类因子限制在 0-50% # 波动率类因子限制在 0-50%
"volat_": (0, 0.5), "volat_": (0, 0.5),
# 换手率类因子限制在 0-500% (实际换手率可能超过100%) # 换手率类因子限制在 0-1000% (实际换手率可能很高)
"turn_": (0, 5.0), "turn_": (0, 10.0),
# 估值评分类因子限制在 -1到1 # 估值评分类因子限制在 -1到1
"val_": (-1.0, 1.0), "val_": (-1.0, 1.0),
# 量价类因子 # 量价类因子
"volume_": (0, 10.0), "volume_": (0, 10.0),
# 市场状态类因子 # 市场状态类因子
"market_": (-1.0, 1.0), "market_": (-1.0, 1.0),
# 技术指标类因子 # 技术指标类因子(放宽范围,允许原始技术指标值)
"tech_": (-1.0, 1.0), "tech_": (-100.0, 100.0),
# 趋势类因子 # 趋势类因子(放宽范围,允许原始趋势指标值)
"trend_": (-1.0, 1.0), "trend_": (-10.0, 10.0),
# 微观结构类因子 # 微观结构类因子
"micro_": (-1.0, 1.0), "micro_": (-1.0, 1.0),
# 情绪类因子 # 情绪类因子