refactor: DeepCoin 1·2단계 파이프라인으로 구조 재편

레거시 분석·매칭·운영 코드를 정리하고 src/deepcoin 기반으로 재구성한다.
1단계 GT는 2년 스윙·눌림목·돌파·다이버전스 타점을 차트에 표시하고,
2단계는 8개 매매 기법과 GT 정합 평가 스크립트를 추가한다.
.env와 GT JSON 산출물은 추적에서 제외한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dsyoon
2026-06-08 23:51:26 +09:00
parent 51f70076fb
commit df3c9aecb9
154 changed files with 4629 additions and 215122 deletions

View File

@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""2단계: Ground Truth 정합 매매 기법 실행 및 비교."""
from __future__ import annotations
import argparse
import logging
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / "src"
if str(SRC) not in sys.path:
sys.path.insert(0, str(SRC))
from deepcoin.config import load_settings
from deepcoin.data.intervals import interval_label
from deepcoin.evaluation.report import (
build_comparison_report,
render_comparison_html,
save_comparison_report,
)
from deepcoin.techniques.base import TechniqueParams
from deepcoin.techniques.registry import list_technique_ids
from deepcoin.techniques.runner import (
load_ground_truth,
run_all_techniques,
save_technique_result,
)
def _configure_logging(verbose: bool) -> None:
"""로깅 레벨을 설정한다."""
level = logging.DEBUG if verbose else logging.INFO
logging.basicConfig(
level=level,
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
def main() -> int:
"""CLI 진입점."""
parser = argparse.ArgumentParser(description="매매 기법 실행 및 GT 정합 비교 (2단계)")
parser.add_argument(
"--techniques",
type=str,
default=None,
help="실행할 기법 ID (쉼표 구분). 기본: 전체",
)
parser.add_argument("--tolerance", type=int, default=None, help="GT 정합 허용 봉 수")
parser.add_argument("--no-report", action="store_true", help="비교 리포트 생략")
parser.add_argument("-v", "--verbose", action="store_true")
args = parser.parse_args()
_configure_logging(args.verbose)
settings = load_settings()
if not settings.ground_truth_file.exists():
logging.error("Ground Truth 파일 없음: %s — 먼저 02_ground_truth.py 실행", settings.ground_truth_file)
return 1
gt_result = load_ground_truth(settings.ground_truth_file)
technique_ids = None
if args.techniques:
technique_ids = [t.strip() for t in args.techniques.split(",") if t.strip()]
params = TechniqueParams(
interval_min=settings.gt_interval_min,
lookback_days=settings.gt_lookback_days,
min_leg_pct=settings.gt_min_leg_pct,
initial_cash_krw=settings.gt_initial_cash_krw,
fee_rate=settings.gt_trading_fee_rate,
extra={"reversal_pct": settings.gt_zigzag_reversal_pct},
)
tolerance = args.tolerance or settings.gt_align_tolerance_bars
logging.info(
"기법 실행: %s %s, %s일, tolerance=%s",
settings.symbol,
interval_label(params.interval_min),
params.lookback_days,
tolerance,
)
results = run_all_techniques(
db_path=settings.db_path,
symbol=settings.symbol,
params=params,
gt_result=gt_result,
tolerance_bars=tolerance,
technique_ids=technique_ids,
)
saved_paths: list[Path] = []
for result in results:
path = save_technique_result(result, settings.techniques_dir)
saved_paths.append(path)
align = result.alignment or {}
legs = align.get("legs", {})
print(
f" [{result.technique_id}] {result.technique_name}: "
f"레그 {result.summary.get('leg_count', 0)}개, "
f"수익 {result.pnl.get('total_return_pct', 0):+.1f}%, "
f"GT정합 score={align.get('score', 0)*100:.1f} "
f"(leg recall {legs.get('leg_recall', 0)*100:.0f}%)"
)
print(f"\n=== 2단계 기법 실행 완료 ({len(results)}개) ===")
print(f"저장: {settings.techniques_dir}/")
for path in saved_paths:
print(f" - {path.name}")
if not args.no_report:
report = build_comparison_report(results, gt_result, settings.symbol)
json_path = save_comparison_report(report, settings.analysis_report_json)
html_path = render_comparison_html(report, settings.analysis_report_html)
print(f"\n=== GT 정합 순위 (상위 3) ===")
gt_return = report["gt"]["return_pct"]
print(f"GT 벤치마크: {gt_return:+.1f}%")
for idx, row in enumerate(report["ranking"][:3], start=1):
print(
f" {idx}. {row['technique_name']}: "
f"score {row['score']*100:.1f}, "
f"수익 {row['tech_return_pct']:+.1f}%, "
f"leg recall {row['leg_recall']*100:.0f}%"
)
print(f"리포트 JSON: {json_path}")
print(f"리포트 HTML: {html_path}")
print(f"\n등록 기법: {', '.join(list_technique_ids())}")
return 0
if __name__ == "__main__":
raise SystemExit(main())