GT MTF 프로필·캘리브레이션과 04 매칭/시뮬/실거래 파이프라인을 추가한다.

3분~일봉 GT 타점 분석(03c), leg 체결 순서 수정, 총자산 90% 검증 루프,
walk-forward Go/No-Go 시뮬, monitor·live_trader 및 reference 문서를 포함한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-31 11:27:50 +09:00
parent b52d61b777
commit 2cb67c42b3
47 changed files with 5956 additions and 209 deletions

View File

@@ -0,0 +1,145 @@
"""
04단계 PDCA 파이프라인: 프로필 → 스캔 → 라벨 → 선별.
"""
from __future__ import annotations
import argparse
import json
import sys
import time
from pathlib import Path
import pandas as pd
from config import CHART_LOOKBACK_DAYS, SYMBOL
from deepcoin.data.mtf_bb import load_frames_from_db
from deepcoin.matching.config import (
MATCHING_BACKTEST_HTML,
MATCHING_FIRE_OUTCOMES,
MATCHING_GT_OVERLAP,
MATCHING_MATCHED_RULES,
MATCHING_RULE_CANDIDATES,
MATCHING_RULE_FIRES,
)
from deepcoin.matching.label_outcomes import label_fire_outcomes
from deepcoin.matching.profile_rules import build_rule_candidates, save_rule_candidates
from deepcoin.matching.rule_eval import (
build_mtf_scan_frame,
conditions_columns,
scan_rule_fires,
)
from deepcoin.matching.select_rules import (
select_matched_rules,
write_backtest_summary_html,
)
from deepcoin.ops.monitor import Monitor
from deepcoin.paths import ensure_dirs
def run_matching_pipeline(
phase: str = "all",
trades_csv: Path | None = None,
) -> None:
"""
04a~04d 단계를 순서대로 실행합니다.
Args:
phase: all | profile | scan | label | select.
trades_csv: 03b CSV 경로(선택).
"""
ensure_dirs()
t0 = time.time()
from config import MATCH_INCLUDE_ATOMIC, MATCH_LABEL_MODE
print(
f"=== 04 매칭 파이프라인 {SYMBOL} "
f"(label={MATCH_LABEL_MODE}, atomic={MATCH_INCLUDE_ATOMIC}) ==="
)
sys.stdout.flush()
candidates_path = MATCHING_RULE_CANDIDATES
fires_path = MATCHING_RULE_FIRES
outcomes_path = MATCHING_FIRE_OUTCOMES
candidates: dict | None = None
fires: pd.DataFrame | None = None
outcomes: pd.DataFrame | None = None
frames = None
if phase in ("all", "profile"):
print("[04] Phase 4-1 Plan/Do: GT 프로필 → 규칙 후보")
candidates = build_rule_candidates(trades_csv)
save_rule_candidates(candidates, candidates_path)
if phase == "profile":
return
if phase in ("all", "scan", "label"):
if candidates is None:
candidates = json.loads(candidates_path.read_text(encoding="utf-8"))
rules = candidates.get("rules", [])
print(f"[04] Phase 4-2 Do: 전구간 발화 스캔 ({len(rules)}규칙)")
sys.stdout.flush()
mon = Monitor(cooldown_file=None)
frames = load_frames_from_db(mon, SYMBOL, lookback_days=CHART_LOOKBACK_DAYS)
needed = conditions_columns(rules)
scan_frame = build_mtf_scan_frame(frames, needed)
fires = scan_rule_fires(scan_frame, rules)
fires_path.parent.mkdir(parents=True, exist_ok=True)
fires.to_csv(fires_path, index=False, encoding="utf-8-sig")
print(f"[04-2] 저장: {fires_path} ({len(fires):,}행)")
if phase == "scan":
return
if phase in ("all", "label", "select"):
if fires is None:
fires = pd.read_csv(fires_path)
if phase in ("all", "label"):
print("[04] Phase 4-3 Check: 발화별 forward PnL 라벨")
if frames is None:
mon = Monitor(cooldown_file=None)
frames = load_frames_from_db(mon, SYMBOL, lookback_days=CHART_LOOKBACK_DAYS)
outcomes = label_fire_outcomes(fires, frames)
outcomes.to_csv(outcomes_path, index=False, encoding="utf-8-sig")
print(f"[04-3] 저장: {outcomes_path}")
if phase == "label":
return
if phase in ("all", "select"):
if candidates is None:
candidates = json.loads(candidates_path.read_text(encoding="utf-8"))
if outcomes is None:
outcomes = pd.read_csv(outcomes_path)
print("[04] Phase 4-4 Act: EV 필터·규칙 선별")
matched = select_matched_rules(outcomes, candidates)
MATCHING_MATCHED_RULES.parent.mkdir(parents=True, exist_ok=True)
MATCHING_MATCHED_RULES.write_text(
json.dumps(matched, ensure_ascii=False, indent=2),
encoding="utf-8",
)
print(f"[04-4] 저장: {MATCHING_MATCHED_RULES}")
overlap = matched.get("gt_overlap", {})
MATCHING_GT_OVERLAP.write_text(
json.dumps(overlap, ensure_ascii=False, indent=2),
encoding="utf-8",
)
write_backtest_summary_html(matched, MATCHING_BACKTEST_HTML)
print(f"완료 ({time.time() - t0:.0f}초)")
def main() -> None:
"""CLI 진입."""
parser = argparse.ArgumentParser(description="04 GT+EV 매칭 파이프라인")
parser.add_argument(
"--phase",
choices=("all", "profile", "scan", "label", "select"),
default="all",
)
parser.add_argument("--trades-csv", type=str, default="")
args = parser.parse_args()
csv = Path(args.trades_csv) if args.trades_csv else None
run_matching_pipeline(phase=args.phase, trades_csv=csv)
if __name__ == "__main__":
main()