Files
qlib/frontend/web/lib/factors.ts
T
Simon f13b34c59e feat(web): 字段库页 + 界面单位换算 + 因子目录参数化 + 选股策略条件表单
字段库与单位:
- `/fields`:字段库管理页(中文名/说明可改、可停用;kind 是引擎事实不可改);
  单位只在字段自己的阶梯里选(总市值 = 万元/亿元),越界 422 原样展示。
- `lib/units.ts`:界面单位 ⇄ 基准单位换算集中一处,条件输入按界面单位回显、
  提交前换回基准单位(引擎只认基准单位,库里存的也永远是基准单位)。
- `SelectionStrategyForm` 取代 `StrategyParamsForm`:一个策略只定义「怎么选」
  (股票池 + 因子 + 过滤条件),条件字段来自字段库接口而不是前端硬编码枚举。

因子参数化(名字即身份,界面不许藏):
- `/factors` 新增「参数」列与展开行:精确引擎键、每个参数的允许范围、来源、依赖列
  (依赖列标明「引擎事实,不可改」);说明文案从「不可修改」改为
  「改参数 = 新建参数化因子 = 新身份,旧因子/既有策略不变义」。
- 「新建参数化因子」卡片:模板下拉 + 受控窗口(min/max)+ 方向枚举下拉,
  实时预览规范键/中文名/渲染公式;后端 422 的原文原样展示,不静默截断。
- 因子下拉显示中文名(含参数)与短键,**value 一律是完整引擎键**;
  停用的因子从各页候选里消失(/strategies、/selection、/signals、/factors/compose),
  既有策略/归档仍按名字解析;`/factors/compose` 顺手修了勾选框点击目标过小。
- `/fields` 加提示:字段库只列内置因子名,参数化因子在 /factors 管理,
  选股策略条件下拉里会一并出现。

tsc --noEmit 0 error;verify_ui_alignment 8 页 160 项全过(另跑 3 个选因子页 51/54)。
2026-10-01 16:38:29 +08:00

51 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 因子目录在界面上的共用小工具(参数化之后才需要)。
*
* 为什么单独一个文件:因子的「名字」在参数化之后变成了带参数的引擎键
* (`momentum(window=90,direction=lower_is_better)`)。它**不能**直接当界面文字用 ——
* 太长、挤爆布局、也没人想读 `direction=higher_is_better`。而「哪些因子能被选」的规则
* (停用的不出现、算不出来的不出现)必须各处一致,否则「停用」在某个页面就成了假开关。
*/
import type { FactorMeta } from "@/lib/types";
/**
* 可以被**选中/引用**的因子。
*
* - `enabled === false`:在目录里停了用 —— 只是不出现在选择列表里,既有策略/归档仍按名字解析。
* - `resolvable === false`:引擎算不出来(历史手工登记行),选了也只会报错,不该摆给人点。
*
* 注意:这**不是**「能不能解析」的判断(那是引擎的事),只是界面候选集的过滤。
*/
export function pickableFactors(list: FactorMeta[]): FactorMeta[] {
return list.filter((f) => f.enabled !== false && f.resolvable !== false);
}
/** 界面显示名:优先后端给的中文名(含参数),没有才退回引擎键。 */
export function factorLabel(f: FactorMeta | undefined, fallback = ""): string {
return f?.label || f?.name || fallback;
}
/**
* 参数化因子的「短键」:只用于展示。
*
* 参数化因子的名字把参数写全了,直接放进下拉会很长;而 direction 在别处(提示里)已经
* 说成「越高越好 / 越低越好」,展示层去掉它不丢信息。**下拉的 value 始终是完整键**
* (引擎身份),只有显示文字被缩短。非参数化名(`momentum_60`)原样返回。
*/
export function shortFactorKey(name: string): string {
const m = /^([A-Za-z_][A-Za-z0-9_]*)\(([^()]*)\)$/.exec(name);
if (!m) return name;
const kept = m[2]
.split(",")
.filter((part) => !part.trim().startsWith("direction="))
.join(",");
return kept ? `${m[1]}(${kept})` : m[1];
}
/** 下拉/列表里的一行文字:中文名(含参数)· 引擎键。藏着名字就等于藏着身份。 */
export function factorOptionLabel(f: FactorMeta): string {
const d = (f.label || f.description).trim();
const head = d.length > 30 ? `${d.slice(0, 30)}…` : d || f.name;
return `${head} · ${shortFactorKey(f.name)}`;
}