# tests/factor/test_fundamental_p1b_library.py """P1-B 批财务因子表达式库: 四新域 6 个注册(5 因子,D14 双注册)契约锁定. 契约: - 表达式裸标识符 ⊆ adapter 特征列 + close/log/cs_rank - 全部 cs_rank 一层截面;E08/E12 负 IC 取负,E11 不定按正向注册待实证 - E13/D14 正向;vb 两因子直接消费 vb 域日频列(不经 close×share_capital) """ import re import sys, os sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "vnpy_v4.4.0"))) import pytest from sanguo_factor import fundamental_library # noqa: F401 import 即注册 from sanguo_factor.fundamental_adapter import FEATURE_COLUMNS from sanguo_factor.fundamental_library import FUNDAMENTAL_P1B_FACTORS from sanguo_factor.registry import list_factors, get_factor @pytest.fixture(autouse=True) def _ensure_fundamental_registered(): """其它测试模块清空 _REGISTRY 后逐测试幂等重注册(顺序依赖前科).""" fundamental_library._register_all() _ALLOWED = set(FEATURE_COLUMNS) | {"close", "cs_rank", "log"} def test_p1b_6_factors_registered(): facs = {f["name"]: f for f in list_factors("fundamental")} names = {n for n, _e, _d, _ic in FUNDAMENTAL_P1B_FACTORS} assert len(names) == 6, f"P1-B 应 6 个注册(5 因子,D14 双注册),实际 {len(names)}" for name in names: assert name in facs, f"{name} 未注册" assert facs[name]["category"] == "fundamental" def test_p1b_expressions_only_reference_feature_columns(): for name, expression, _d, _ic in FUNDAMENTAL_P1B_FACTORS: idents = set(re.findall(r"[A-Za-z_][A-Za-z0-9_]*", expression)) bad = idents - _ALLOWED assert not bad, f"{name} 引用了未产出列: {bad} in {expression}" def test_p1b_all_cross_sectional_rank_one_layer(): for name, expression, _d, _ic in FUNDAMENTAL_P1B_FACTORS: assert expression.startswith("cs_rank("), f"{name}: {expression}" assert expression.endswith(")") def test_p1b_negative_ic_factors_flipped(): """E08(CCC 低=效率高)/E12(户数降=筹码集中)取负;E13/D14/E11 正向.""" assert get_factor("fund_ccc")["expression"] == "cs_rank(-ccc)" assert get_factor("fund_gdhs_chg")["expression"] == "cs_rank(-gdhs_chg)" assert get_factor("fund_topholder_chg")["expression"] == "cs_rank(topholder_chg)" # E11 方向不定(公告前+/除权后−混合)→ 正向注册,由 IC 实测定去留 assert get_factor("fund_send_total_12m")["expression"] == "cs_rank(send_total_12m)" def test_p1b_vb_factors_consume_vb_columns(): """D14 双注册直接消费 vb 域列(长史 1990 起含退市,分域验证用).""" assert get_factor("fund_ep_vb")["expression"] == "cs_rank(ep_vb)" assert get_factor("fund_bp_vb")["expression"] == "cs_rank(bp_vb)" def test_p1b_registration_idempotent(): fundamental_library._register_all() fundamental_library._register_all() # P0 32 + P1 37 + P1-B 6 = 75 assert len(list_factors("fundamental")) == 75