hybrid DD tier와 Option C 2차(+1000%) 검증을 추가하고 실거래 사이징을 정합한다.

인과 GT leg 엔진·drawdown tier·train 캘리브레이션, Phase 2 Go/No-Go 및 시뮬 리포트를 반영한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
xavis
2026-06-01 16:09:18 +09:00
parent 9b00ef34c6
commit d385456867
21 changed files with 3315 additions and 1178 deletions

View File

@@ -202,6 +202,13 @@ def _summary_cards_html(
sim_trade_count: int,
go_flag: bool,
model_note: str = "",
sim_causal_gt_pnl: dict[str, Any] | None = None,
sim_causal_hybrid_pnl: dict[str, Any] | None = None,
sim_tier_enhanced_pnl: dict[str, Any] | None = None,
sim_primary_pnl: dict[str, Any] | None = None,
primary_sizing: str = "",
hybrid_go: bool = False,
phase2_go: bool = False,
) -> str:
"""
ground_truth HTML과 동일 구성의 상단 카드 (GT + 시뮬 2줄).
@@ -216,6 +223,12 @@ def _summary_cards_html(
sim_trade_count: 체결 가정 발화 수.
go_flag: Go/No-Go.
model_note: GT 모델 한 줄 요약.
sim_causal_gt_pnl: 인과 GT leg 엔진 요약.
sim_causal_hybrid_pnl: monitor buy + 인과 sell 하이브리드.
sim_tier_enhanced_pnl: monitor + conviction tier.
sim_primary_pnl: 검증 통과 권장 배분 경로.
primary_sizing: hybrid | causal_tier.
hybrid_go: hybrid tier Go/No-Go.
Returns:
cards HTML.
@@ -235,6 +248,61 @@ def _summary_cards_html(
f'<span class="{go_cls}">{"GO" if go_flag else "NO-GO"}</span>'
)
sim_fixed_title = f"시뮬·고정 ₩{LIVE_ORDER_KRW:,}/회 (비교)"
primary_block = ""
if sim_primary_pnl and float(sim_primary_pnl.get("pnl_pct") or 0) != 0:
hgo_cls = "go-pass" if hybrid_go else "go-fail"
primary_block = stacked_summary_cards_html(
(
f"권장 primary · {primary_sizing or 'causal_tier'} · "
f'<span class="{hgo_cls}">hybrid {"GO" if hybrid_go else "NO-GO"}</span> · '
f'2차 {"GO" if phase2_go else "NO-GO"}'
),
pnl_cards_html(
sim_primary_pnl,
"Primary",
int(sim_primary_pnl.get("trade_count") or 0),
),
)
causal_block = ""
if sim_causal_gt_pnl and (
float(sim_causal_gt_pnl.get("pnl_pct") or 0) != 0
or sim_causal_gt_pnl.get("trade_count")
):
legs = sim_causal_gt_pnl.get("leg_count", "-")
causal_block += stacked_summary_cards_html(
f"인과 GT leg 엔진 (peak_local·분할매수·causal tier) · leg {legs}",
pnl_cards_html(
sim_causal_gt_pnl,
"인과 GT",
int(sim_causal_gt_pnl.get("trade_count") or 0),
),
)
if sim_causal_hybrid_pnl and (
float(sim_causal_hybrid_pnl.get("pnl_pct") or 0) != 0
or sim_causal_hybrid_pnl.get("trade_count")
):
legs_h = sim_causal_hybrid_pnl.get("leg_count", "-")
causal_block += stacked_summary_cards_html(
f"하이브리드 (monitor + DD tier) · 발화 {sim_causal_hybrid_pnl.get('input_fires', '-')}",
pnl_cards_html(
sim_causal_hybrid_pnl,
"하이브리드",
int(sim_causal_hybrid_pnl.get("trade_count") or 0),
),
)
if sim_tier_enhanced_pnl and (
float(sim_tier_enhanced_pnl.get("pnl_pct") or 0) != 0
or sim_tier_enhanced_pnl.get("trade_count")
):
ast = sim_tier_enhanced_pnl.get("alloc_stats") or {}
causal_block += stacked_summary_cards_html(
f"Enhanced tier (conviction·DD) · large매수 {ast.get('large_tier_buy_count', '-')}",
pnl_cards_html(
sim_tier_enhanced_pnl,
"Enhanced",
int(sim_tier_enhanced_pnl.get("trade_count") or 0),
),
)
return (
'<div class="summary-cards">'
+ stacked_summary_cards_html(gt_sub, gt_cards)
@@ -246,6 +314,8 @@ def _summary_cards_html(
sim_fixed_title,
pnl_cards_html(sim_fixed_pnl, "시뮬(고정)", sim_trade_count),
)
+ primary_block
+ causal_block
+ "</div>"
)
@@ -298,6 +368,11 @@ def build_simulation_page_html(
go = report.get("go_no_go", {})
go_flag = bool(go.get("go"))
go_hybrid = report.get("go_no_go_hybrid") or {}
hybrid_go_flag = bool(go_hybrid.get("go"))
go_phase2 = report.get("go_no_go_option_c_phase2") or {}
phase2_go_flag = bool(go_phase2.get("go"))
pc = report.get("portfolio_compare") or {}
label_mode = report.get("label_mode", "leg_gt")
frames = load_chart_frames()
@@ -372,6 +447,36 @@ def build_simulation_page_html(
criteria_blocks = "".join(rule_criteria_html(r) for r in monitor_rules)
go_table = go_no_go_table_html(go.get("checks", []), go_flag)
hybrid_checks = go_hybrid.get("checks") or []
if hybrid_checks:
hybrid_rows = "".join(
f"<tr><td>{c.get('name')}</td>"
f"<td>{'PASS' if c.get('pass') else 'FAIL'}</td>"
f"<td>{c.get('value', '-')}</td></tr>"
for c in hybrid_checks
)
hgo_cls = "go-pass" if hybrid_go_flag else "go-fail"
go_table += (
f"<h2>Hybrid tier Go/No-Go · "
f'<span class="{hgo_cls}">{"GO" if hybrid_go_flag else "NO-GO"}</span></h2>'
f"<table><thead><tr><th>검사</th><th>결과</th><th>값</th></tr></thead>"
f"<tbody>{hybrid_rows}</tbody></table>"
)
phase2_checks = go_phase2.get("checks") or []
if phase2_checks:
p2_rows = "".join(
f"<tr><td>{c.get('name')}</td>"
f"<td>{'PASS' if c.get('pass') else 'FAIL'}</td>"
f"<td>{c.get('value', '-')}</td></tr>"
for c in phase2_checks
)
p2_cls = "go-pass" if phase2_go_flag else "go-fail"
go_table += (
f"<h2>Option C 2차 (+1000%) · "
f'<span class="{p2_cls}">{"GO" if phase2_go_flag else "NO-GO"}</span></h2>'
f"<table><thead><tr><th>검사</th><th>결과</th><th>값</th></tr></thead>"
f"<tbody>{p2_rows}</tbody></table>"
)
def _mark_note(price: float) -> str:
if price > 0:
@@ -439,6 +544,13 @@ def build_simulation_page_html(
len(compound_fires),
go_flag,
model_note=model_note,
sim_causal_gt_pnl=pc.get("sim_causal_gt"),
sim_causal_hybrid_pnl=pc.get("sim_causal_hybrid"),
sim_tier_enhanced_pnl=pc.get("sim_tier_enhanced"),
sim_primary_pnl=pc.get("sim_primary"),
primary_sizing=str(pc.get("primary_sizing") or ""),
hybrid_go=hybrid_go_flag,
phase2_go=phase2_go_flag,
)
if frames is not None: