Files
sanguo_vnpy_v2/sanguo_factor/composite_library.py
T
claude_dev e330e130d4 chore(factor): P1二轮回评修缮——v1.1口径残留对齐+导入示例纠偏+replace_strict [nas]
- composite_library docstring/测试注释: 7源/19源/fund7/all19 残留 → 6源/18源
  (7dfcaca 剔 topholder 后文档未同步,防后续按旧口径引用)
- import_runs_to_main_db 示例 --dst data_backup→data(09-08 导错库坑,
  示例即陷阱原文,加⚠️一行防再踩)
- forecast 预告类型映射 replace→replace_strict(消 398 条 DeprecationWarning,
  参数语义等价,231 测全绿)
- P1 晨报§五: 补 09-09 只读探针复核(首期 20210930/5431文件每期)+§19.10
  修订文本入档(master 侧一行改,本分支无该节)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-09 12:12:00 +08:00

110 lines
5.2 KiB
Python

"""合成层 v1(Combiner): 量价 12 独立源 + 财务 6 源 → 等权 rank 合成总分因子.
「因子战法八环」第④环首个落地. 因子 = 表达式字符串注册(category="composite"),
表达式由 build 函数从源清单常量表循环拼接(18 源数千字符,引擎 eval 解析无碍;
可读性/可维护性由源常量表承载,不手写长表达式).
cs_rank 语义(vnpy_v4.4.0/vnpy/alpha/dataset/cs_function.py:15):
pl.col("data").rank().over("datetime") —— 按 datetime 分组、组内全池
截面 rank(batch_eval alpha_df 为全池长表,即每日全市场截面排名,正确口径;
polars rank 默认 average 法,值域 1..N 非归一化,N=当日非空源值数).
方向口径(钉死,二者 IC 等价——每日仅差截面常数,不影响秩相关):
- 量价源: 注册表达式为原始连续值(无方向) → build 统一包 cs_rank 截面化;
负向源项 = `(-1) * cs_rank((<expr>))`,负号在 rank 外(与 alpha16 引擎先例
`-1 * cs_rank(...)` 同族);备选 `cs_rank((-1) * (...))`(rank 内取负=秩反转)
弃用,全文统一 rank 外.
- 财务源: 注册表达式本身已是 `cs_rank(指标)`(负 IC 已在表达式内取负定向,
高=好) → 直接内嵌、不再套 cs_rank;源表方向列 = 内嵌后净方向,登记 "-"
时才前置 `(-1) * `(当前 6 源全为已定向正向,不触发).
Null 传播: polars 算术 null 传染 → 合成值任一源 null 即 null(覆盖 = 18 源
交集;gdhs 等事件流源在上市早期覆盖受限,合成因子覆盖期短于
单源,属已知限制非缺陷).
warmup: 量价源最大窗 vma_60=60 bar / alpha83 内层 ts_sum(ts_mean(volume,10),50)
≈ 59 bar < batch_eval WARMUP_BARS=120,统一预热掩码覆盖.
"""
from .registry import register_factor, get_factor, _REGISTRY
# 量价 12 独立源(08-31 族分析固化;方向 = 该源 ICIR 符号)
QUANT_SOURCES: list[tuple[str, str]] = [
("vma_60", "+"), # alpha158 量能收缩(族1 代表族)
("alpha16", "+"), # alpha101 量价背离(族2 代表,全池最强单源)
("alpha83", "+"), # alpha101
("alpha12", "+"), # alpha101
("alpha2", "+"), # alpha101
("alpha42", "+"), # alpha101
("vol_ma5", "-"), # builtin
("wvma_20", "-"), # alpha158
("klow", "-"), # alpha158
("cord_5", "-"), # alpha158
("kup", "-"), # alpha158
("alpha81", "-"), # alpha101
]
# 财务 6 源(P0+P1 IC 结论 + 族分析独立性选定;注册表达式已 cs_rank 定向).
# v1.1: 剔 fund_topholder_chg —— top_holders 源数据 2021Q3 起(NAS 镜像实测,2020Q1
# 契约口径与实际不符),首期无差分 → 特征 2022 年中才生效;null 传染使合成因子在
# h1(2016-2021)整窗空序列(实测 months=0).引擎无 fill_null 类算子,无法表达式内
# null-safe 平均,故 v1 剔除;v2 数据补全或特征层合成时回填.
FUND_SOURCES: list[tuple[str, str]] = [
("fund_bp", "+"), # h2 ICIR 0.319 唯一 effective
("fund_nsi", "+"), # h2 0.233
("fund_gp_over_m", "+"), # h2 0.212 新晋
("fund_gdhs_chg", "+"), # 独立源 0.15/0.11
("fund_growth_scissors", "+"), # h1 0.236(h2 0.076 衰减但方向稳)
("fund_sue_np", "+"), # h1 0.200
]
# (合成名, 源清单)——项形态由源注册 category 决定: fundamental=内嵌,其余=cs_rank 包裹
COMPOSITE_FACTORS: list[tuple[str, list[tuple[str, str]]]] = [
("composite_quant12", QUANT_SOURCES),
("composite_fund6", FUND_SOURCES),
("composite_all18", QUANT_SOURCES + FUND_SOURCES),
]
def rank_term(expression: str, direction: str) -> str:
"""原始值源贡献项: cs_rank 截面化;负向 = rank 外前置 (-1) *(口径钉死)."""
term = f"cs_rank(({expression}))"
return f"(-1) * {term}" if direction == "-" else term
def _embed_term(expression: str, direction: str) -> str:
"""已定向源(财务)贡献项: 原样内嵌;净方向 "-" 时才前置负号."""
return f"({expression})" if direction == "+" else f"(-1) * ({expression})"
def build_composite_expression(terms: list[str]) -> str:
"""等权平均拼接: (项1 + 项2 + ...) / N."""
return "(" + " + ".join(terms) + f") / {len(terms)}"
def _register_all() -> None:
"""注册全部合成因子(幂等);先幂等补挂源(测试清空 _REGISTRY 后可独立重建)."""
from . import library as _builtin_lib
from . import alpha_datasets as _alpha_ds
from . import fundamental_library as _fund_lib
_builtin_lib._register_all()
_alpha_ds.mount_all()
_fund_lib._register_all()
for name, sources in COMPOSITE_FACTORS:
if name in _REGISTRY:
continue
terms = []
for src_name, direction in sources:
src = get_factor(src_name)
if src is None:
raise ValueError(f"合成源未注册: {src_name}")
if src["category"] == "fundamental":
terms.append(_embed_term(src["expression"], direction))
else:
terms.append(rank_term(src["expression"], direction))
register_factor(name, build_composite_expression(terms), category="composite")
# 模块导入时自动注册(与 library.py/fundamental_library.py 同模式)
_register_all()