feat(web): 因子研究升级 + 因子组合页

- 因子元数据增加 brief 使用简介(后端 FactorDef + /api/factors 返回,9 个内置因子补齐文案)
- 因子研究页:
  · 时间默认「今日 ~ 近 6 个月」
  · 因子多选,逐个跑 IC/RankIC Job 并分卡展示报告
  · 目录显示使用简介,行点击展开公式/方向/用法详解
  · 新增计分规则说明与「去因子组合」入口
- 因子组合页 /factors/compose:多因子勾选+权重、组合得分规则说明(z-score×权重求和→TopN),
  可配 topN/调仓/区间/ST,一键回测 Job → 净值/回撤/月度/未建模标注
- 回测页时间默认同样近 6 月
- 验证:tsc + next build 通过;组合回测(momentum_60×1.5 + volatility_60×1.0)Job 成功归档
  EXP-330CAAAE(-13.51%,交易 20);后端 factors/research/api 相关测试通过 / ruff clean
This commit is contained in:
Simon
2026-09-07 21:36:16 +08:00
parent 1cf7c84897
commit a77d3c13c3
10 changed files with 519 additions and 97 deletions
+1
View File
@@ -15,6 +15,7 @@ def list_factor_catalog() -> list[dict]:
{
"name": d.name,
"description": d.description,
"brief": d.brief,
"formula": d.formula,
"frequency": d.frequency,
"lookback": d.lookback,
+3 -1
View File
@@ -130,7 +130,9 @@ def get_settings() -> Settings:
except (TypeError, ValueError):
job_memory_limit_gb = 6
try:
job_max_concurrent = int(os.environ.get("QLIB_JOB_MAX_CONCURRENT") or job_cfg.get("max_concurrent_jobs") or 2)
job_max_concurrent = int(
os.environ.get("QLIB_JOB_MAX_CONCURRENT") or job_cfg.get("max_concurrent_jobs") or 2
)
except (TypeError, ValueError):
job_max_concurrent = 2
+26 -3
View File
@@ -21,6 +21,7 @@ class FactorDef:
name: str
description: str
formula: str
brief: str = "" # 一句话使用简介(面向用户:怎么用、什么时候有效)
frequency: str = "daily"
lookback: int = 20
direction: str = "higher_is_better" # | lower_is_better
@@ -82,14 +83,26 @@ def _rolling_vol(prices: pd.DataFrame, lookback: int) -> pd.DataFrame:
@register(
FactorDef("momentum_20", "过去 20 个交易日收益率", "close / close.shift(20) - 1", lookback=20)
FactorDef(
"momentum_20",
"过去 20 个交易日收益率",
"close / close.shift(20) - 1",
brief="短期动量:近一个月强势股延续性较强,适合趋势延续环境(牛市中段);震荡市易追高。",
lookback=20,
)
)
def _momentum_20(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
return _rolling_return(fields["close"], 20)
@register(
FactorDef("momentum_60", "过去 60 个交易日收益率", "close / close.shift(60) - 1", lookback=60)
FactorDef(
"momentum_60",
"过去 60 个交易日收益率",
"close / close.shift(60) - 1",
brief="中期动量:A 股常见有效时段(约 1~3 个月),趋势行情首选;需结合市场阶段判断方向。",
lookback=60,
)
)
def _momentum_60(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
return _rolling_return(fields["close"], 60)
@@ -97,7 +110,11 @@ def _momentum_60(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
@register(
FactorDef(
"momentum_120", "过去 120 个交易日收益率", "close / close.shift(120) - 1", lookback=120
"momentum_120",
"过去 120 个交易日收益率",
"close / close.shift(120) - 1",
brief="长期动量:反映近半年强势,适合大级别趋势;换手慢、回撤修复慢,弱市慎用。",
lookback=120,
)
)
def _momentum_120(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
@@ -109,6 +126,7 @@ def _momentum_120(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
"volatility_20",
"过去 20 个交易日收益率波动率",
"std(pct_change, 20)",
brief="低波防御(方向 lower_is_better):近月波动小的股票抗跌,弱市/熊市阶段相对占优。",
lookback=20,
direction="lower_is_better",
)
@@ -122,6 +140,7 @@ def _volatility_20(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
"volatility_60",
"过去 60 个交易日收益率波动率",
"std(pct_change, 60)",
brief="低波动(方向 lower_is_better):近一季低波组合长期回测常有超额,是防御型核心因子。",
lookback=60,
direction="lower_is_better",
)
@@ -135,6 +154,7 @@ def _volatility_60(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
"close_to_high_60",
"收盘价相对 60 日最高价的接近程度",
"close / rolling_max(high, 60)",
brief="贴近 60 日高点(接近新高):趋势确认型强势股,常与动量互补;需配合市场热度判断。",
lookback=60,
requires=("close", "high"),
)
@@ -149,6 +169,7 @@ def _close_to_high_60(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
"volume_ratio_5_60",
"量比:5 日均量 / 60 日均量",
"mean(volume, 5) / mean(volume, 60)",
brief="量比放大提示资金关注(短线活跃型);高换手也伴随更高波动,注意与波动因子搭配。",
lookback=60,
requires=("volume",),
)
@@ -163,6 +184,7 @@ def _volume_ratio_5_60(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
"ma_bias_20",
"20 日均线乖离率",
"(close - ma(close, 20)) / ma(close, 20)",
brief="20 日均线乖离:上行趋势中正乖离偏强;乖离过大易回落,需警惕过热。",
lookback=20,
)
)
@@ -177,6 +199,7 @@ def _ma_bias_20(fields: dict[str, pd.DataFrame]) -> pd.DataFrame:
"reversal_5",
"短期反转:过去 5 日收益率取负(越低越接近超跌)",
"-1 * (close / close.shift(5) - 1)",
brief="短期反转(方向 higher_is_better):前期跌幅大的超跌反弹机会,适合震荡/修复行情。",
lookback=5,
)
)