"""2단계: 인과 기법 GT 정합 비교 리포트.""" from __future__ import annotations import json from datetime import datetime from pathlib import Path from typing import Any from deepcoin.techniques.base import TechniqueResult def build_comparison_report( results: list[TechniqueResult], gt_result: dict[str, Any], symbol: str, ) -> dict[str, Any]: """기법 비교 요약 리포트를 생성한다.""" gt_meta = gt_result.get("meta", {}) gt_pnl = gt_result.get("pnl", {}) gt_summary = gt_result.get("summary", {}) rows: list[dict[str, Any]] = [] for result in results: align = result.alignment or {} buy = align.get("buy", {}) sell = align.get("sell", {}) legs = align.get("legs", {}) rows.append( { "technique_id": result.technique_id, "technique_name": result.technique_name, "category": result.category, "causal": result.causal, "leg_count": result.summary.get("leg_count", 0), "tech_return_pct": result.pnl.get("total_return_pct", 0.0), "buy_recall": buy.get("recall", 0.0), "sell_recall": sell.get("recall", 0.0), "leg_recall": legs.get("leg_recall", 0.0), "return_capture_ratio": align.get("return_capture_ratio", 0.0), "score": align.get("score", 0.0), "avg_buy_offset": buy.get("avg_bar_offset", 0.0), "avg_sell_offset": sell.get("avg_bar_offset", 0.0), } ) rows.sort(key=lambda r: r["score"], reverse=True) return { "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "symbol": symbol, "gt": { "leg_count": gt_summary.get("leg_count", 0), "return_pct": gt_pnl.get("total_return_pct", 0.0), "interval_label": gt_meta.get("interval_label", ""), "lookback_days": gt_meta.get("lookback_days", 365), }, "ranking": rows, } def save_comparison_report(report: dict[str, Any], json_path: Path) -> Path: """비교 리포트 JSON을 저장한다.""" json_path.parent.mkdir(parents=True, exist_ok=True) with json_path.open("w", encoding="utf-8") as fp: json.dump(report, fp, ensure_ascii=False, indent=2) return json_path def render_comparison_html(report: dict[str, Any], html_path: Path) -> Path: """비교 리포트 HTML을 생성한다.""" html_path.parent.mkdir(parents=True, exist_ok=True) rows = report.get("ranking", []) gt = report.get("gt", {}) table_rows = "" for idx, row in enumerate(rows, start=1): table_rows += f""" {idx} {row['technique_name']} {row['category']} {'Y' if row['causal'] else 'N'} {row['leg_count']} {row['tech_return_pct']:+.1f}% {row['buy_recall']*100:.1f}% {row['sell_recall']*100:.1f}% {row['leg_recall']*100:.1f}% {row['return_capture_ratio']*100:.1f}% {row['score']*100:.1f} """ html = f""" DeepCoin 2단계 — 인과 GT 정합

DeepCoin 2단계 — 인과 기법 Ground Truth 정합

생성: {report.get('generated_at', '')} | {report.get('symbol', '')} | GT: {gt.get('leg_count', 0)}레그, {gt.get('return_pct', 0):+.1f}% | 기간: 최근 {gt.get('lookback_days', 365)}일
{table_rows}
순위기법유형인과레그 수익률매수 Recall매도 Recall레그 Recall 수익 포착종합 Score
""" with html_path.open("w", encoding="utf-8") as fp: fp.write(html) return html_path