from __future__ import annotations import json from dataclasses import asdict from pathlib import Path import pandas as pd from dragon_branch_configs import alpha_first_selective_veto_config def _load_csv(base_dir: Path, name: str) -> pd.DataFrame: return pd.read_csv(base_dir / name, encoding="utf-8-sig") def _format_pct(value: float) -> str: if pd.isna(value): return "NA" if value == float("inf"): return "inf" return f"{value:.2%}" def _format_num(value: float) -> str: if pd.isna(value): return "NA" if value == float("inf"): return "inf" return f"{value:.2f}" def main() -> None: base_dir = Path(__file__).resolve().parent audit = _load_csv(base_dir, "dragon_short_holding_audit.csv") pressure = _load_csv(base_dir, "dragon_short_holding_family_pressure.csv") experiments = _load_csv(base_dir, "dragon_short_holding_experiments.csv") best = experiments[experiments["experiment"] != "baseline_alpha_first"].sort_values( ["avg_return", "profit_factor"], ascending=[False, False] ).iloc[0] winner_config = alpha_first_selective_veto_config().with_updates( glued_selective_hot_c1_min=40.0, glued_selective_hot_b1_min=0.10, glued_selective_low_c1_min=23.0, glued_selective_low_c1_max=28.0, glued_selective_low_b1_max=0.02, ) snapshot = asdict(winner_config) snapshot["disabled_rules"] = sorted(winner_config.disabled_rules) (base_dir / "dragon_short_holding_candidate_config.json").write_text( json.dumps(snapshot, indent=2, ensure_ascii=False) + "\n", encoding="utf-8", ) root_summary = ( audit.groupby("failure_root") .agg(trades=("buy_date", "count"), avg_return=("return_pct", "mean")) .reset_index() .sort_values("trades", ascending=False) ) top_entry_drag = pressure[pressure["group_type"] == "entry_family"].sort_values("drag_score", ascending=False).head(5) top_path_drag = pressure[pressure["group_type"] == "path_combo"].sort_values("drag_score", ascending=False).head(5) lines = [ "# Dragon Short Holding Master Review", "", "- Branch under audit: `alpha_first_selective_veto`.", "- Goal: identify the dominant short-holding drag and the next narrow optimization target.", "", "## Audit Conclusions", f"- audited short trades: `{int(len(audit))}`", ] for _, row in root_summary.iterrows(): lines.append( f"- failure_root `{row['failure_root']}`: trades `{int(row['trades'])}`, avg_return `{_format_pct(float(row['avg_return']))}`" ) lines.extend(["", "## Lead Drag Families"]) for _, row in top_entry_drag.iterrows(): lines.append( f"- `{row['holding_bucket']} / {row['entry_family']}`: trades `{int(row['trades'])}`, " f"avg_return `{_format_pct(float(row['avg_return']))}`, drag_score `{row['drag_score']:.4f}`" ) lines.extend(["", "## Lead Drag Paths"]) for _, row in top_path_drag.iterrows(): lines.append( f"- `{row['holding_bucket']} / {row['entry_family']} -> {row['sell_reason']}`: trades `{int(row['trades'])}`, " f"avg_return `{_format_pct(float(row['avg_return']))}`, drag_score `{row['drag_score']:.4f}`" ) lines.extend( [ "", "## Experiment Winner", f"- best branch: `{best['experiment']}`", f"- trades: `{int(best['trades'])}`", f"- avg_return: `{_format_pct(float(best['avg_return']))}`", f"- profit_factor: `{_format_num(float(best['profit_factor']))}`", f"- short_avg_return: `{_format_pct(float(best['short_avg_return']))}`", f"- `00-05d`: `{_format_pct(float(best['short_00_05d_avg_return']))}`", f"- `06-10d`: `{_format_pct(float(best['short_06_10d_avg_return']))}`", f"- real BUY / SELL overlap: `{int(best['real_buy_overlap'])}/{int(best['real_sell_overlap'])}`", "", "## Interpretation", "- `post_sell_rebound_buy` is not the main short-holding problem in this pack; disabling it hurts or adds little value.", "- The dominant short-holding drag is `glued_buy`, especially in mid-regime short exits.", "- The winning branch confirms that narrow glued-entry veto is more valuable than attacking `post_sell_rebound_buy` first.", "- The most useful next alpha-first direction is now a glued-focused selective-veto branch, not a post-sell-rebound branch.", "", "## Candidate Config", "- Snapshot file: `dragon_short_holding_candidate_config.json`.", "- This candidate should remain alpha-first research only until a branch-level governance decision upgrades it.", ] ) (base_dir / "dragon_short_holding_master_review.md").write_text("\n".join(lines) + "\n", encoding="utf-8") if __name__ == "__main__": main()