from __future__ import annotations import argparse from pathlib import Path from typing import Any import yaml ROOT = Path(__file__).resolve().parents[1] def _load_yaml(path: Path) -> dict[str, Any]: if not path.exists(): return {} payload = yaml.safe_load(path.read_text(encoding="utf-8")) return payload if isinstance(payload, dict) else {} def _domain_for(formula_id: str, formula: dict[str, Any]) -> str: fid = formula_id.upper() text = " ".join([fid, str(formula.get("purpose", "")), str(formula.get("canonical_ref", "")), str(formula.get("agents_md_ref", ""))]).upper() if any(k in text for k in ["CASH", "SHORTFALL", "FLOOR", "RECOVERY", "RAISE", "LIQUIDITY", "SLIPPAGE"]): return "cash" if any(k in text for k in ["ENTRY", "BREAKOUT", "CHASE", "ALPHA", "TIMING", "FOLLOW_THROUGH", "TRANCHE", "PULLBACK"]): return "entry" if any(k in text for k in ["EXIT", "SELL", "STOP", "TAKE_PROFIT", "WATERFALL", "REBOUND", "PRESERVATION", "TRAILING"]): return "exit" if any(k in text for k in ["PORTFOLIO", "POSITION", "HEAT", "BETA", "SECTOR", "REGIME", "WEIGHT", "CONCENTRATION", "DRAWDOWN"]): return "portfolio" if any(k in text for k in ["REPORT", "RUNTIME", "DASHBOARD", "LEDGER", "AUDIT", "TRACE", "NARRATIVE", "QUALITY", "PROOF", "DECISION"]): return "reporting" return "risk" def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--in", dest="input_path", default="spec/13_formula_registry.yaml") parser.add_argument("--out", dest="out_dir", default="spec/formulas") args = parser.parse_args() src = ROOT / args.input_path out_dir = ROOT / args.out_dir payload = _load_yaml(src) formulas = ((payload.get("formula_registry") or {}).get("formulas")) or {} buckets: dict[str, dict[str, Any]] = {k: {"schema_version": "formula_domain.v1", "source": str(src), "domain": k, "formulas": {}} for k in ["risk", "entry", "exit", "cash", "portfolio", "reporting", "fundamental", "smart_money", "macro"]} for fid, formula in formulas.items(): domain = _domain_for(str(fid), formula if isinstance(formula, dict) else {}) # Auto-populate required contract fields f_dict = dict(formula) if isinstance(formula, dict) else {} if "owner" not in f_dict: f_dict["owner"] = "quant_team" if "lifecycle_state" not in f_dict: f_dict["lifecycle_state"] = "active" if "input_fields" not in f_dict: inputs = f_dict.get("inputs") or [] f_dict["input_fields"] = [inp["field"] for inp in inputs if isinstance(inp, dict) and "field" in inp] if "output_fields" not in f_dict: out = f_dict.get("output") if isinstance(out, dict) and "field" in out: f_dict["output_fields"] = [out["field"]] else: f_dict["output_fields"] = [] if "missing_policy" not in f_dict: f_dict["missing_policy"] = "DATA_MISSING. 계산 결과를 추정하지 않는다." if "golden_cases" not in f_dict: f_dict["golden_cases"] = [] if "activation_threshold" not in f_dict: f_dict["activation_threshold"] = {"min_t20_sample": 30} if "retirement_condition" not in f_dict: f_dict["retirement_condition"] = "performance_degradation" buckets[domain]["formulas"][fid] = f_dict out_dir.mkdir(parents=True, exist_ok=True) for domain, doc in buckets.items(): (out_dir / f"{domain}.yaml").write_text(yaml.safe_dump(doc, sort_keys=False, allow_unicode=True), encoding="utf-8") manifest = { "schema_version": "formula_domain_manifest.v1", "source": str(src), "domains": {domain: f"spec/formulas/{domain}.yaml" for domain in buckets}, "formula_count": len(formulas), } (out_dir / "manifest.yaml").write_text(yaml.safe_dump(manifest, sort_keys=False, allow_unicode=True), encoding="utf-8") print(yaml.safe_dump(manifest, sort_keys=False, allow_unicode=True).strip()) return 0 if __name__ == "__main__": raise SystemExit(main())