caa72d3426
- run_batch_eval 增 fund_data_dir 参数;仅因子列表含 category=fundamental 时 build_fundamental_features(bars 的 codes+trading_dates) join 进 alpha_df (列白名单经 join 扩展,量价批零开销零改动路径) - __init__ 补挂 fundamental_library(import sanguo_factor 全家可见) - 端到端3测: 财务4因子IC落库/量价+财务混批/纯量价无静态域回归 - 修顺序依赖: 其它测试清 _REGISTRY 只重挂alpha → 本批两模块 autouse 幂等重注册 [nas] Co-Authored-By: Claude Code <noreply@anthropic.com>
102 lines
4.2 KiB
Python
102 lines
4.2 KiB
Python
# tests/factor/test_fundamental_batch.py
|
||
"""财务因子批端到端: 合成行情库 + 合成静态域 → batch_eval 出 IC 指标."""
|
||
import sqlite3
|
||
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 alpha_datasets # 挂载量价因子
|
||
from sanguo_factor import fundamental_library # noqa: F401 挂载财务因子(import 即注册)
|
||
from sanguo_factor.batch_eval import run_batch_eval
|
||
from sanguo_factor import eval_store
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def _ensure_fundamental_registered():
|
||
"""其它测试模块清空 _REGISTRY 后只重挂 alpha/builtin(顺序依赖前科),
|
||
这里逐测试幂等重注册财务因子,保证本模块与顺序无关."""
|
||
fundamental_library._register_all()
|
||
|
||
|
||
_DDL = """
|
||
CREATE TABLE dbbardata(
|
||
symbol TEXT, exchange TEXT, datetime TEXT, interval TEXT,
|
||
volume REAL, turnover REAL, open_interest REAL,
|
||
open_price REAL, high_price REAL, low_price REAL, close_price REAL)
|
||
"""
|
||
|
||
_STOCKS = [("600000", "SSE"), ("000001", "SZSE"), ("300001", "SZSE")]
|
||
|
||
|
||
@pytest.fixture(scope="module")
|
||
def db(tmp_path_factory):
|
||
"""3 只 × 2022-06~2024-01 合成日线(与合成财务域同代码;趋势构造截面差异)."""
|
||
alpha_datasets.mount_all()
|
||
p = tmp_path_factory.mktemp("fb")
|
||
db = str(p / "qt.db")
|
||
conn = sqlite3.connect(db)
|
||
conn.execute(_DDL)
|
||
import pandas as pd
|
||
days = pd.bdate_range("2022-06-01", "2024-01-05")
|
||
drift = {"600000": 0.0012, "000001": -0.0006, "300001": 0.0004}
|
||
base = {"600000": 10.0, "000001": 5.0, "300001": 20.0}
|
||
for i, day in enumerate(days):
|
||
d = day.strftime("%Y-%m-%d")
|
||
rows = []
|
||
for sym, ex in _STOCKS:
|
||
px = base[sym] * (1.0 + drift[sym]) ** i
|
||
rows.append((sym, ex, f"{d} 00:00:00", "d", 100.0, px * 100.0, 0,
|
||
px, px, px, px))
|
||
conn.executemany("INSERT INTO dbbardata VALUES(?,?,?,?,?,?,?,?,?,?,?)", rows)
|
||
conn.commit()
|
||
conn.close()
|
||
return db
|
||
|
||
|
||
def test_fundamental_end_to_end(db, synthetic_static, tmp_path):
|
||
eval_db = str(tmp_path / "fund_eval.db")
|
||
out = run_batch_eval(
|
||
factor_names=["fund_roe_ttm", "fund_tacc", "fund_ep_ttm",
|
||
"fund_forecast_type"],
|
||
start="2023-02-01", end="2023-12-31",
|
||
eval_db=eval_db, label="fund_t", cfg=None, vnpy_db_override=db,
|
||
fund_data_dir=synthetic_static,
|
||
)
|
||
assert out["factors_done"] == 4
|
||
assert out["errors"] == []
|
||
for name in ("fund_roe_ttm", "fund_tacc", "fund_ep_ttm", "fund_forecast_type"):
|
||
m = eval_store.get_detail(eval_db, out["run_id"], name)["metrics"]
|
||
assert "error" not in m, f"{name}: {m.get('error')}"
|
||
# 截面 3 只 → 逐日 rank IC 可算(预告因子 2023-07-15 后才有效, count 少但非零)
|
||
assert isinstance(m["1"]["ic_mean"], float) or m["1"]["ic_mean"] is None
|
||
assert m["1"]["count"] >= 0
|
||
|
||
|
||
def test_mixed_batch_price_plus_fundamental(db, synthetic_static, tmp_path):
|
||
"""量价+财务同批混跑: 白名单 join 只在含财务因子时发生,互不干扰."""
|
||
eval_db = str(tmp_path / "mix_eval.db")
|
||
out = run_batch_eval(
|
||
factor_names=["ma_20", "fund_sue_np"],
|
||
start="2023-02-01", end="2023-06-30",
|
||
eval_db=eval_db, label="mix_t", cfg=None, vnpy_db_override=db,
|
||
fund_data_dir=synthetic_static,
|
||
)
|
||
assert out["errors"] == []
|
||
rows = eval_store.get_rows(eval_db, out["run_id"])
|
||
assert {r["factor"] for r in rows} == {"ma_20", "fund_sue_np"}
|
||
# SUE 在 2023-06-30 前可见的报告期均无完整 8 期窗 → 全 NaN → eliminated 非 error
|
||
m = eval_store.get_detail(eval_db, out["run_id"], "fund_sue_np")["metrics"]
|
||
assert "error" not in m
|
||
|
||
|
||
def test_pure_price_batch_untouched(db, tmp_path):
|
||
"""纯量价批不传静态域照常跑(回归: 未新增强依赖)."""
|
||
eval_db = str(tmp_path / "px_eval.db")
|
||
out = run_batch_eval(
|
||
factor_names=["roc_5"], start="2023-02-01", end="2023-03-31",
|
||
eval_db=eval_db, label="px_t", cfg=None, vnpy_db_override=db,
|
||
)
|
||
assert out["errors"] == []
|
||
assert out["factors_done"] == 1
|