feat(api): 因子详情端点透传 icStats 全期弹药(无报告如实 None)+monthly_dir env 覆盖 [vps]

This commit is contained in:
2026-10-10 10:20:45 +08:00
parent 86bfbcf283
commit 73ba705bca
2 changed files with 34 additions and 0 deletions
+5
View File
@@ -605,6 +605,10 @@ def factor_detail(name: str) -> dict:
birth = {"jobId": job["jobId"], "startedAt": job["startedAt"],
"rounds": job["rounds"], "hypId": hyp_id}
break
stats = None
reports = _monthly_reports()
if reports:
stats = (reports[0][2].get("factor_stats") or {}).get(name)
return {"factor": {
"name": name, "status": e["status"],
"origin": e.get("origin") or "manual", "hypothesis": hyp_id,
@@ -615,6 +619,7 @@ def factor_detail(name: str) -> dict:
"createdAt": first.get("effective_from"),
"version": str(first.get("v")) if first else "1",
"icRecentT": (points.get(name) or {}).get("t"),
"icStats": stats,
"lastEvalDate": last_eval}, "birth": birth}
+29
View File
@@ -126,3 +126,32 @@ def test_graveyard_factor_list_endpoint(client, tmp_path):
assert got["fa_old"]["cause"] == "IC 两窗反号(09-22 终判)"
# 活跃因子不出现在墓园列表
assert "fa_gross_margin" not in got
def test_factor_detail_ic_stats(client, tmp_path, monkeypatch):
"""detail 端点带出 factor_stats(全期弹药),无报告时如实 None."""
import json as _json
name, _ = _seed_factor_card(client, name="fa_x") # 注册活因子(assessable)
rep_dir = tmp_path / "reports" / "factor_monthly"
rep_dir.mkdir(parents=True)
doc = {"as_of": "2026-09-30",
"monthly_points": {"fa_x": [
{"month": "2025-09", "ic_mean": 0.04, "t": 2.1, "count": 1},
{"month": "2025-10", "ic_mean": 0.06, "t": 2.4, "count": 1},
{"month": "2026-09", "ic_mean": 0.02, "t": 1.1, "count": 1}]},
"factor_stats": {"fa_x": {"icAll": 0.04, "tAll": 1.7321,
"positiveRatio": 1.0,
"byYear": {"2025": 0.05, "2026": 0.02}}}}
(rep_dir / "nas_2026-09-30.json").write_text(_json.dumps(doc), "utf-8")
monkeypatch.setenv("SANGUO_FACTOR_MONTHLY_DIR", str(rep_dir))
r = client.get(f"/api/v1/pipeline/factors/{name}/detail")
assert r.status_code == 200
got = r.json()["factor"]["icStats"]
assert got["icAll"] == 0.04 and got["tAll"] == 1.7321
assert got["byYear"]["2025"] == 0.05
# 无报告时的反向用例:指到空目录,icStats 如实 None(不造数)
empty = tmp_path / "reports_empty"
empty.mkdir()
monkeypatch.setenv("SANGUO_FACTOR_MONTHLY_DIR", str(empty))
r2 = client.get(f"/api/v1/pipeline/factors/{name}/detail")
assert r2.status_code == 200 and r2.json()["factor"]["icStats"] is None