84 lines
3.7 KiB
Python
84 lines
3.7 KiB
Python
#!/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())
|