Files
sanguo_vnpy_v2/tests/factor/test_fundamental_p1b_library.py
T
claude_dev 66619c17d8 feat(factor): P1-B批library注册6因子(四新域5因子+D14双注册) [nas]
fund_ccc(-)/fund_send_total_12m(不定正向注册待实证)/fund_gdhs_chg(-)/
fund_topholder_chg(+)/fund_ep_vb(+)/fund_bp_vb(+);注册总数 69→75
(P0 32+P1 37+P1-B 6,既有计数契约同步更新);batch e2e 锁死 vb+dividend
域级引用瘦身 join 路径。
2026-09-09 12:12:00 +08:00

73 lines
3.0 KiB
Python
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.
# 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