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