"""Build a license-free synthetic cache (bars -> grids) and manifest so the whole pipeline runs without FinData. Usage: make_synthetic.py [--cache data/synthetic/cache] [--n 4] [--start 2024-01-02 --end 2024-05-31]""" from __future__ import annotations import argparse import hashlib import json import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) from chronos_x.data.synthetic import synthetic_bars # noqa: E402 from chronos_x.data.features import build_grid, save_grid # noqa: E402 ROOT = Path(__file__).resolve().parents[1] def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("--cache", default="data/synthetic/cache"); ap.add_argument("--n", type=int, default=4) ap.add_argument("--start", default="2024-01-02"); ap.add_argument("--end", default="2024-05-31") a = ap.parse_args() cache = ROOT / a.cache inst = [] for i in range(a.n): sym = f"SYN{i:02d}"; ac = "etf" if i % 4 == 3 else "stock" df, actions = synthetic_bars(sym, a.start, a.end, seed=100 + i, p_missing=0.02 + 0.03 * (i % 3), split=("2024-03-04", 2.0) if i == 1 else None, sigma=3e-4 * (1 + 0.5 * i), asset_class=ac) arrays, meta = build_grid(df, actions, a.start, a.end, sym, ac) save_grid(arrays, meta, cache / "grids" / f"{a.start}_{a.end}" / sym) inst.append({"symbol": sym, "asset_class": ac, "role": "heldout_instrument" if i == a.n - 1 else "pooled", "adv_usd": 1e7 * (i + 1), "adv_tercile": ["low", "mid", "high"][i % 3], "vol_tercile": ["low", "mid", "high"][(i + 1) % 3], "group": "synthetic", "delisted": False, "status": "active", "first_date": a.start, "last_date": a.end}) man = {"config": {"synthetic": True}, "instruments": inst, "pilot_symbols": [r["symbol"] for r in inst if r["role"] == "pooled"], "feasibility_symbols": [r["symbol"] for r in inst if r["role"] == "pooled"], "n_selected": len(inst)} man["sha256"] = hashlib.sha256(json.dumps(man, sort_keys=True).encode()).hexdigest() (ROOT / "data" / "manifests").mkdir(parents=True, exist_ok=True) (ROOT / "data" / "manifests" / "universe_synthetic.json").write_text(json.dumps(man, indent=1)) print(f"synthetic cache at {cache} ({a.n} instruments, {meta.counts['sessions']} sessions)") if __name__ == "__main__": main()