feat(api): 因子详情端点透传 icStats 全期弹药(无报告如实 None)+monthly_dir env 覆盖 [vps]
This commit is contained in:
@@ -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}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user