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:
145
deepcoin/matching/pipeline.py
Normal file
145
deepcoin/matching/pipeline.py
Normal 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()
|
||||
Reference in New Issue
Block a user