Files
sanguo_vnpy_v2/scripts/factor_research/run_eval.py
T

84 lines
3.7 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.
#!/usr/bin/env python
"""批量因子评估 CLI(冒烟/全量;NAS docker exec / 本地直跑).
用法示例:
冒烟(50只×2024×10因子):
venv310/bin/python scripts/factor_research/run_eval.py \
--start 2024-01-01 --end 2024-12-31 --limit 50 --label smoke-2024 \
--factors alpha2,alpha6,alpha12,alpha18,kmid,klen,roc_5,ma_20,std_20,wvma_20
全量(Alpha101+158 × 全A × 8.5年):
venv310/bin/python scripts/factor_research/run_eval.py --label batch1-full
"""
import argparse
import os
import sys
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")))
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("--start", default="2018-01-01")
ap.add_argument("--end", default="2026-06-30")
ap.add_argument("--categories", nargs="*", default=["alpha101", "alpha158"])
ap.add_argument("--factors", default="", help="逗号分隔,优先于 --categories")
ap.add_argument("--symbols", default="", help="逗号分隔裸代码;空=全A")
ap.add_argument("--limit", type=int, default=None, help="随机抽样 N 只(种子42)")
ap.add_argument("--label", default="batch1")
ap.add_argument("--db", default=None, help="factor_eval.db 路径;默认 default_eval_db_path()")
ap.add_argument("--run-id", default=None, help="断点续跑:复用既有 run_id,跳过已落库因子")
ap.add_argument("--list-factors", default="", metavar="CATEGORY", help="列出类目因子后退出")
args = ap.parse_args()
from sanguo_factor.alpha_datasets import mount_all
mount_all()
from sanguo_factor.registry import list_factors
from sanguo_factor.batch_eval import run_batch_eval
from sanguo_factor.eval_store import default_eval_db_path, get_rows
if args.list_factors:
for f in list_factors(args.list_factors):
print(f"{f['name']:16s} [{f['category']}] {f['expression'][:80]}")
return 0
if args.factors:
factor_names = [s.strip() for s in args.factors.split(",") if s.strip()]
else:
factor_names = [f["name"] for c in args.categories for f in list_factors(c)]
if not factor_names:
print("未找到因子(检查 --categories/--factors)", file=sys.stderr)
return 1
db_path = args.db or default_eval_db_path()
symbols = [s.strip() for s in args.symbols.split(",") if s.strip()] or None
print(f"[eval] {len(factor_names)} 因子 × {args.start}~{args.end}{db_path}")
def _cb(done: int, total: int, current: str) -> None:
print(f"[eval] {done}/{total} {current}", flush=True)
out = run_batch_eval(factor_names, args.start, args.end, db_path, label=args.label,
symbols=symbols, limit=args.limit, cfg=None, progress_cb=_cb, run_id=args.run_id)
print(f"[eval] run_id={out['run_id']} done={out['factors_done']} "
f"errors={len(out['errors'])} elapsed={out['elapsed_sec']}s symbols={out['symbols_count']}")
if out["errors"]:
print(f"[eval] 失败因子: {', '.join(out['errors'][:20])}")
rows = get_rows(db_path, out["run_id"])
scored = []
for r in rows:
p1 = (r.get("metrics") or {}).get("1") or {}
if p1.get("icir") is not None:
scored.append((abs(p1["icir"]), r["factor"], p1))
scored.sort(reverse=True)
print(f"\n{'因子':<14s} {'IC':>8s} {'ICIR':>8s} {'t':>8s} {'胜率':>7s} 结论")
for _, name, p1 in scored[:15]:
print(f"{name:<14s} {p1['ic_mean']:>8.4f} {p1['icir']:>8.3f} "
f"{p1['t_stat']:>8.2f} {p1['win_rate']:>7.1%} {p1['conclusion']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())