llm-quant/app/features/extended_factors.py
2025-10-05 10:22:25 +08:00

195 lines
7.2 KiB
Python

"""Extended factor implementations for the quant system.
This module contains additional high-quality factors that extend the default factor set.
All factors are designed to be lightweight and programmatically generated to meet
end-to-end automated decision-making requirements.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, List, Sequence
from app.core.indicators import momentum, volatility, rolling_mean, normalize
@dataclass
class FactorSpec:
"""Specification for a factor computation.
Attributes:
name: Factor name identifier
window: Required lookback window (0 for snapshot-only factors)
"""
name: str
window: int
# Extended factors focusing on momentum, value, and liquidity signals
EXTENDED_FACTORS: List[FactorSpec] = [
# 增强动量因子
FactorSpec("mom_10_30", 0), # 10日与30日动量差
FactorSpec("mom_5_20_rank", 0), # 相对排名动量因子
FactorSpec("mom_dynamic", 0), # 动态窗口动量因子
# 波动率相关因子
FactorSpec("volat_5", 5), # 短期波动率
FactorSpec("volat_ratio", 0), # 长短期波动率比率
# 换手率扩展因子
FactorSpec("turn_60", 60), # 长期换手率
FactorSpec("turn_rank", 0), # 换手率相对排名
# 价格均线比率因子
FactorSpec("price_ma_10_ratio", 0), # 当前价格与10日均线比率
FactorSpec("price_ma_20_ratio", 0), # 当前价格与20日均线比率
FactorSpec("price_ma_60_ratio", 0), # 当前价格与60日均线比率
# 成交量均线比率因子
FactorSpec("volume_ma_5_ratio", 0), # 当前成交量与5日均线比率
FactorSpec("volume_ma_20_ratio", 0), # 当前成交量与20日均线比率
# 高级估值因子
FactorSpec("val_ps_score", 0), # PS估值评分
FactorSpec("val_multiscore", 0), # 综合估值评分
FactorSpec("val_dividend_score", 0), # 股息率估值评分
# 市场状态因子
FactorSpec("market_regime", 0), # 市场状态因子
FactorSpec("trend_strength", 0), # 趋势强度因子
]
def compute_extended_factor_values(
close_series: Sequence[float],
volume_series: Sequence[float],
turnover_series: Sequence[float],
latest_fields: Dict[str, float],
) -> Dict[str, float]:
"""Compute values for extended factors.
Args:
close_series: Closing prices series (most recent first)
volume_series: Trading volume series (most recent first)
turnover_series: Turnover rate series (most recent first)
latest_fields: Latest available fields including valuation ratios
Returns:
Dictionary mapping factor names to computed values
"""
if not close_series:
return {}
results: Dict[str, float] = {}
# 增强动量因子
# 10日与30日动量差
if len(close_series) >= 30:
mom_10 = momentum(close_series, 10)
mom_30 = momentum(close_series, 30)
if mom_10 is not None and mom_30 is not None:
results["mom_10_30"] = mom_10 - mom_30
# 相对排名动量因子
# 这里需要市场数据来计算相对排名,暂时使用简化版本
if len(close_series) >= 20:
mom_20 = momentum(close_series, 20)
if mom_20 is not None:
# 简化处理:将动量标准化
results["mom_5_20_rank"] = min(1.0, max(0.0, (mom_20 + 0.2) / 0.4))
# 动态窗口动量因子
# 根据波动率动态调整窗口
if len(close_series) >= 20:
volat_20 = volatility(close_series, 20)
mom_20 = momentum(close_series, 20)
if volat_20 is not None and mom_20 is not None and volat_20 > 0:
# 波动率调整后的动量
results["mom_dynamic"] = mom_20 / volat_20
# 波动率相关因子
# 短期波动率
if len(close_series) >= 5:
results["volat_5"] = volatility(close_series, 5)
# 长短期波动率比率
if len(close_series) >= 20 and len(close_series) >= 5:
volat_5 = volatility(close_series, 5)
volat_20 = volatility(close_series, 20)
if volat_5 is not None and volat_20 is not None and volat_20 > 0:
results["volat_ratio"] = volat_5 / volat_20
# 换手率扩展因子
# 长期换手率
if len(turnover_series) >= 60:
results["turn_60"] = rolling_mean(turnover_series, 60)
# 换手率相对排名
if len(turnover_series) >= 20:
turn_20 = rolling_mean(turnover_series, 20)
if turn_20 is not None:
# 简化处理:将换手率标准化
results["turn_rank"] = min(1.0, max(0.0, turn_20 / 5.0)) # 假设5%为高换手率
# 价格均线比率因子
if len(close_series) >= 10:
ma_10 = rolling_mean(close_series, 10)
if ma_10 is not None and ma_10 > 0:
results["price_ma_10_ratio"] = close_series[0] / ma_10
if len(close_series) >= 20:
ma_20 = rolling_mean(close_series, 20)
if ma_20 is not None and ma_20 > 0:
results["price_ma_20_ratio"] = close_series[0] / ma_20
if len(close_series) >= 60:
ma_60 = rolling_mean(close_series, 60)
if ma_60 is not None and ma_60 > 0:
results["price_ma_60_ratio"] = close_series[0] / ma_60
# 成交量均线比率因子
if len(volume_series) >= 5:
vol_ma_5 = rolling_mean(volume_series, 5)
if vol_ma_5 is not None and vol_ma_5 > 0:
results["volume_ma_5_ratio"] = volume_series[0] / vol_ma_5
if len(volume_series) >= 20:
vol_ma_20 = rolling_mean(volume_series, 20)
if vol_ma_20 is not None and vol_ma_20 > 0:
results["volume_ma_20_ratio"] = volume_series[0] / vol_ma_20
# 高级估值因子
ps = latest_fields.get("daily_basic.ps")
if ps is not None and ps > 0:
# PS估值评分
results["val_ps_score"] = 2.5 / (2.5 + ps) # 使用2.5作为scale参数
pe = latest_fields.get("daily_basic.pe")
pb = latest_fields.get("daily_basic.pb")
# 综合估值评分
scores = []
if pe is not None and pe > 0:
scores.append(12.0 / (12.0 + pe)) # PE评分
if pb is not None and pb > 0:
scores.append(2.5 / (2.5 + pb)) # PB评分
if ps is not None and ps > 0:
scores.append(2.5 / (2.5 + ps)) # PS评分
if scores:
results["val_multiscore"] = sum(scores) / len(scores)
dv_ratio = latest_fields.get("daily_basic.dv_ratio")
if dv_ratio is not None:
# 股息率估值评分
results["val_dividend_score"] = min(1.0, max(0.0, dv_ratio / 5.0)) # 假设5%为高股息率
# 市场状态因子
# 简单的市场状态指标:基于价格位置
if len(close_series) >= 60:
ma_60 = rolling_mean(close_series, 60)
if ma_60 is not None and ma_60 > 0:
results["market_regime"] = close_series[0] / ma_60
# 趋势强度因子
if len(close_series) >= 20:
mom_20 = momentum(close_series, 20)
volat_20 = volatility(close_series, 20)
if mom_20 is not None and volat_20 is not None and volat_20 > 0:
# 趋势强度:动量与波动率的比率
results["trend_strength"] = abs(mom_20) / volat_20
return results