refactor: Git에서 데이터 제거, 설정·코드만 유지
파이프라인 산출물(data/, docs/)을 Git 추적에서 제외하고 히스토리를 단일 커밋으로 재구성해 저장소 용량을 경량화한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
132
src/deepcoin/evaluation/report.py
Normal file
132
src/deepcoin/evaluation/report.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""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"""
|
||||
<tr>
|
||||
<td>{idx}</td>
|
||||
<td>{row['technique_name']}</td>
|
||||
<td>{row['category']}</td>
|
||||
<td>{'Y' if row['causal'] else 'N'}</td>
|
||||
<td>{row['leg_count']}</td>
|
||||
<td>{row['tech_return_pct']:+.1f}%</td>
|
||||
<td>{row['buy_recall']*100:.1f}%</td>
|
||||
<td>{row['sell_recall']*100:.1f}%</td>
|
||||
<td>{row['leg_recall']*100:.1f}%</td>
|
||||
<td>{row['return_capture_ratio']*100:.1f}%</td>
|
||||
<td><strong>{row['score']*100:.1f}</strong></td>
|
||||
</tr>"""
|
||||
|
||||
html = f"""<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>DeepCoin 2단계 — 인과 GT 정합</title>
|
||||
<style>
|
||||
body {{ font-family: "Malgun Gothic", Arial, sans-serif; margin: 24px; color: #333; background: #f5f5f5; }}
|
||||
h1 {{ font-size: 20px; margin-bottom: 8px; }}
|
||||
.meta {{ color: #666; margin-bottom: 20px; font-size: 14px; }}
|
||||
table {{ border-collapse: collapse; width: 100%; background: #fff; }}
|
||||
th, td {{ border: 1px solid #ddd; padding: 8px 10px; text-align: center; font-size: 13px; }}
|
||||
th {{ background: #e8e8e8; }}
|
||||
tr:nth-child(even) {{ background: #fafafa; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>DeepCoin 2단계 — 인과 기법 Ground Truth 정합</h1>
|
||||
<div class="meta">
|
||||
생성: {report.get('generated_at', '')} |
|
||||
{report.get('symbol', '')} |
|
||||
GT: {gt.get('leg_count', 0)}레그, {gt.get('return_pct', 0):+.1f}% |
|
||||
기간: 최근 {gt.get('lookback_days', 365)}일
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>순위</th><th>기법</th><th>유형</th><th>인과</th><th>레그</th>
|
||||
<th>수익률</th><th>매수 Recall</th><th>매도 Recall</th><th>레그 Recall</th>
|
||||
<th>수익 포착</th><th>종합 Score</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{table_rows}
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
with html_path.open("w", encoding="utf-8") as fp:
|
||||
fp.write(html)
|
||||
return html_path
|
||||
Reference in New Issue
Block a user