1#!/usr/bin/env python32"""Expand the frozen configs into a job list (instrument x model x distribution x estimation variant).3usage: make_jobs.py --name main [--tier A,B] [--symbols ...] [--oos-start 2012-01-03] [--oos-end 2026-10-30]4 [--window 1000] [--refit 5] [--expanding] [--models m1,m2]"""5from __future__ import annotations67import argparse8import json9import sys10from pathlib import Path1112import yaml1314ROOT = Path(__file__).resolve().parents[1]15sys.path.insert(0, str(ROOT / "src"))161718def main() -> None:19 ap = argparse.ArgumentParser()20 ap.add_argument("--name", required=True)21 ap.add_argument("--tier", default="A,B")22 ap.add_argument("--symbols", default="")23 ap.add_argument("--models", default="")24 ap.add_argument("--oos-start", default=None)25 ap.add_argument("--oos-end", default=None)26 ap.add_argument("--window", type=int, default=None)27 ap.add_argument("--refit", type=int, default=None)28 ap.add_argument("--expanding", action="store_true")29 ap.add_argument("--status", default="implemented", help="comma-separated registry statuses to include (implemented,extension)")30 a = ap.parse_args()31 reg = yaml.safe_load(open(ROOT / "configs" / "model_registry.yaml"))["models"]32 inst = yaml.safe_load(open(ROOT / "configs" / "instruments.yaml"))33 prot = yaml.safe_load(open(ROOT / "configs" / "forecast_protocol.yaml"))34 uni = inst.get("universe") or inst["candidates"]35 syms = [s for cls in uni for s in uni[cls]]36 if a.symbols:37 syms = [s for s in a.symbols.split(",") if s]38 tiers = a.tier.split(",")39 oos_start = a.oos_start or prot["periods"]["validation"]["start"]40 oos_end = a.oos_end or prot["periods"]["test"]["end"]41 window = a.window or prot["estimation"]["window"]42 refit = a.refit or prot["estimation"]["refit_every"]43 want = set(a.models.split(",")) if a.models else None44 jobs = []45 for s in syms:46 for mid, m in reg.items():47 if m["status"] not in a.status.split(",") or m["tier"] not in tiers or (want and mid not in want):48 continue49 for dist in m["dists"]:50 jobs.append(dict(symbol=s, model_id=mid, dist=dist, tier=m["tier"], window=window, refit_every=refit,51 expanding=a.expanding, rm_proxy=prot["tier_b_input"], oos_start=oos_start, oos_end=oos_end,52 seed=prot["estimation"]["seed"], max_restarts=prot["estimation"]["max_restarts"]))53 # heavy jobs first (better load balance): msgarch, figarch, realgarch, aparch/egarch (simulation forecasts)54 rank = {"msgarch2": 0, "figarch11": 1, "realgarch11": 2, "aparch11": 3, "egarch11": 4, "tarch11": 5, "avgarch11": 6}55 jobs.sort(key=lambda j: (rank.get(j["model_id"], 9), j["symbol"]))56 out = ROOT / "results" / "manifests" / f"jobs_{a.name}.json"57 out.write_text(json.dumps(jobs, indent=0))58 print(out, len(jobs), "jobs;", len(syms), "symbols;", sorted({j['model_id'] for j in jobs}))596061if __name__ == "__main__":62 main()63