GT 총자산 비율 매수·leg 티어 배분과 시뮬/실거래 포지션 사이징을 통합한다.
타점·비중을 gt_model로 일반화하고, amount_krw 시각순 배분·EV/WF·상위 leg 대형 매수를 position_sizing과 시뮬 HTML(고정 ₩/회 비교)에 반영한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -20,6 +20,7 @@ from config import (
|
||||
)
|
||||
from deepcoin.ground_truth.ground_truth import (
|
||||
load_ground_truth,
|
||||
order_trades_chronological,
|
||||
simulate_truth_portfolio,
|
||||
simulate_truth_portfolio_steps,
|
||||
)
|
||||
@@ -28,6 +29,7 @@ from deepcoin.matching.portfolio_sim import (
|
||||
select_capped_fires,
|
||||
simulate_fixed_order_portfolio,
|
||||
simulate_fixed_order_portfolio_steps,
|
||||
simulate_sized_portfolio,
|
||||
)
|
||||
from deepcoin.matching.select_rules import _split_train_valid_holdout
|
||||
from deepcoin.ops.chart_report import (
|
||||
@@ -36,6 +38,7 @@ from deepcoin.ops.chart_report import (
|
||||
market_cards_html,
|
||||
pnl_cards_html,
|
||||
rule_criteria_html,
|
||||
stacked_summary_cards_html,
|
||||
wrap_chart_report_page,
|
||||
)
|
||||
from deepcoin.ops.simulation import build_chart_html, load_chart_frames, _frames_to_mtf
|
||||
@@ -194,9 +197,11 @@ def _summary_cards_html(
|
||||
bb_txt: str,
|
||||
gt_trades: list[dict[str, Any]],
|
||||
gt_pnl: dict[str, Any],
|
||||
sim_pnl: dict[str, Any],
|
||||
sim_sized_pnl: dict[str, Any],
|
||||
sim_fixed_pnl: dict[str, Any],
|
||||
sim_trade_count: int,
|
||||
go_flag: bool,
|
||||
model_note: str = "",
|
||||
) -> str:
|
||||
"""
|
||||
ground_truth HTML과 동일 구성의 상단 카드 (GT + 시뮬 2줄).
|
||||
@@ -206,26 +211,43 @@ def _summary_cards_html(
|
||||
bb_txt: BB %B.
|
||||
gt_trades: GT trades.
|
||||
gt_pnl: GT 포트폴리오 요약.
|
||||
sim_pnl: 시뮬 포트폴리오 요약.
|
||||
sim_sized_pnl: 총자산%·EV/WF·leg 시뮬 요약.
|
||||
sim_fixed_pnl: 고정 ₩/회 baseline.
|
||||
sim_trade_count: 체결 가정 발화 수.
|
||||
go_flag: Go/No-Go.
|
||||
model_note: GT 모델 한 줄 요약.
|
||||
|
||||
Returns:
|
||||
cards HTML.
|
||||
"""
|
||||
go_cls = "go-pass" if go_flag else "go-fail"
|
||||
gt_row = (
|
||||
'<p class="cards-group-title">정답 (ground_truth) — 분할 비중·leg 체결</p>'
|
||||
+ market_cards_html(close_last, bb_txt)
|
||||
+ pnl_cards_html(gt_pnl, "정답 타점", len(gt_trades))
|
||||
gt_sub = (
|
||||
"저점 분할매수(1/price 비중) · 고점 65/35% 매도 · "
|
||||
"총자산×비중×leg티어 · 시각순 복리"
|
||||
)
|
||||
sim_row = (
|
||||
'<p class="cards-group-title">시뮬 (monitor_rules · holdout · '
|
||||
f"1회 ₩{LIVE_ORDER_KRW:,.0f}·일한도) — "
|
||||
f'<span class="{go_cls}">{"GO" if go_flag else "NO-GO"}</span></p>'
|
||||
+ pnl_cards_html(sim_pnl, "시뮬 체결", sim_trade_count)
|
||||
if model_note:
|
||||
gt_sub = model_note
|
||||
gt_cards = market_cards_html(close_last, bb_txt) + pnl_cards_html(
|
||||
gt_pnl, "정답 GT", len(gt_trades)
|
||||
)
|
||||
sim_sized_title = (
|
||||
"시뮬·총자산% (EV/WF·leg상위) — "
|
||||
f'<span class="{go_cls}">{"GO" if go_flag else "NO-GO"}</span>'
|
||||
)
|
||||
sim_fixed_title = f"시뮬·고정 ₩{LIVE_ORDER_KRW:,}/회 (비교)"
|
||||
return (
|
||||
'<div class="summary-cards">'
|
||||
+ stacked_summary_cards_html(gt_sub, gt_cards)
|
||||
+ stacked_summary_cards_html(
|
||||
sim_sized_title,
|
||||
pnl_cards_html(sim_sized_pnl, "시뮬(비율)", sim_trade_count),
|
||||
)
|
||||
+ stacked_summary_cards_html(
|
||||
sim_fixed_title,
|
||||
pnl_cards_html(sim_fixed_pnl, "시뮬(고정)", sim_trade_count),
|
||||
)
|
||||
+ "</div>"
|
||||
)
|
||||
return gt_row + sim_row
|
||||
|
||||
|
||||
def build_simulation_page_html(
|
||||
@@ -294,56 +316,57 @@ def build_simulation_page_html(
|
||||
elif gt_summary.get("mark_price"):
|
||||
close_val = float(gt_summary["mark_price"])
|
||||
|
||||
sim_trades = fires_to_trade_list(capped)
|
||||
gt_pnl = {}
|
||||
gt_summary_pnl = gt_data.get("summary") or {}
|
||||
if gt_summary_pnl.get("pnl_krw") is not None and gt_summary_pnl.get(
|
||||
"execution_order"
|
||||
) == "leg_block":
|
||||
gt_pnl = {
|
||||
k: gt_summary_pnl[k]
|
||||
for k in (
|
||||
"initial_cash_krw",
|
||||
"final_asset_krw",
|
||||
"pnl_pct",
|
||||
"total_fees_krw",
|
||||
"holding_qty",
|
||||
"holding_value_krw",
|
||||
"mark_price",
|
||||
"cash_krw",
|
||||
)
|
||||
if k in gt_summary_pnl
|
||||
}
|
||||
elif gt_trades:
|
||||
sim_trades_sized = fires_to_trade_list(capped, apply_dynamic_sizing=True)
|
||||
sim_trades_fixed = fires_to_trade_list(capped, apply_dynamic_sizing=False)
|
||||
|
||||
gt_pnl: dict[str, Any] = {}
|
||||
if gt_trades:
|
||||
gt_chron = order_trades_chronological(gt_trades)
|
||||
gt_pnl = simulate_truth_portfolio(
|
||||
gt_trades,
|
||||
gt_chron,
|
||||
initial_cash=GT_INITIAL_CASH_KRW,
|
||||
fee_rate=TRADING_FEE_RATE,
|
||||
last_price=close_val if close_val else None,
|
||||
)
|
||||
|
||||
sim_pnl = simulate_fixed_order_portfolio(
|
||||
sim_trades,
|
||||
mark = close_val if close_val else None
|
||||
sim_sized_pnl = simulate_sized_portfolio(
|
||||
sim_trades_sized,
|
||||
initial_cash=GT_INITIAL_CASH_KRW,
|
||||
fee_rate=TRADING_FEE_RATE,
|
||||
last_price=mark,
|
||||
)
|
||||
sim_fixed_pnl = simulate_fixed_order_portfolio(
|
||||
sim_trades_fixed,
|
||||
order_krw=LIVE_ORDER_KRW,
|
||||
initial_cash=GT_INITIAL_CASH_KRW,
|
||||
fee_rate=TRADING_FEE_RATE,
|
||||
last_price=close_val if close_val else None,
|
||||
last_price=mark,
|
||||
sizing_mode="fixed",
|
||||
)
|
||||
sim_steps = simulate_fixed_order_portfolio_steps(
|
||||
sim_trades,
|
||||
sim_trades_sized,
|
||||
order_krw=LIVE_ORDER_KRW,
|
||||
initial_cash=GT_INITIAL_CASH_KRW,
|
||||
fee_rate=TRADING_FEE_RATE,
|
||||
sizing_mode="amount_krw",
|
||||
)
|
||||
gt_steps = (
|
||||
simulate_truth_portfolio_steps(
|
||||
gt_trades,
|
||||
order_trades_chronological(gt_trades),
|
||||
initial_cash=GT_INITIAL_CASH_KRW,
|
||||
fee_rate=TRADING_FEE_RATE,
|
||||
)
|
||||
if gt_trades
|
||||
else []
|
||||
)
|
||||
model = gt_data.get("model") or {}
|
||||
model_note = (
|
||||
f"mode={model.get('selection_mode', 'split_buy_peak_sell')} · "
|
||||
f"매수비중=1/price · 매도=65/35%"
|
||||
if model
|
||||
else ""
|
||||
)
|
||||
|
||||
criteria_blocks = "".join(rule_criteria_html(r) for r in monitor_rules)
|
||||
go_table = go_no_go_table_html(go.get("checks", []), go_flag)
|
||||
@@ -355,7 +378,7 @@ def build_simulation_page_html(
|
||||
|
||||
sim_table = f"""
|
||||
<h2>시뮬 타점 (holdout {len(holdout_fires)}건 → 체결 가정 {len(capped)}건)</h2>
|
||||
<p class="meta">1회 ₩{LIVE_ORDER_KRW:,.0f}·일한도·최대 거래수 적용 후 체결 순 포트폴리오.
|
||||
<p class="meta">총자산×최적비중·현금한도·EV/WF통과·leg상위 대형 매수. 일한도·최대 거래수 적용.
|
||||
가격 열 (+/-) = <b>{label_mode}</b> 구간 수익%.{_mark_note(close_val)}</p>
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
@@ -367,7 +390,7 @@ def build_simulation_page_html(
|
||||
|
||||
gt_table = f"""
|
||||
<h2>정답 타점 (ground_truth)</h2>
|
||||
<p class="meta">삼각형 = GT. 매수 분할 비중·매도 leg 반영.{_mark_note(close_val)}</p>
|
||||
<p class="meta">삼각형 크기 = GT 체결 금액. 매수 분할·매도 leg 반영.{_mark_note(close_val)}</p>
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead><tr><th>시각</th><th>구분</th><th>비중</th><th>가격</th><th>총 평가금액</th><th>해석</th></tr></thead>
|
||||
@@ -390,7 +413,7 @@ def build_simulation_page_html(
|
||||
"상단 카드: 초기 금액·총보유자산·초기 대비 증감율·수수료."
|
||||
)
|
||||
legend = (
|
||||
"▲ <b>정답 매수</b> · ▼ <b>정답 매도</b> — 삼각형 = GT 비중.<br>"
|
||||
"▲ <b>정답 매수</b> · ▼ <b>정답 매도</b> — 삼각형 = GT 체결 금액.<br>"
|
||||
"● <b>시뮬</b> — 원 = holdout 발화 (차트). 테이블 = 일한도 적용 체결 순서."
|
||||
)
|
||||
if frames is not None:
|
||||
@@ -405,7 +428,15 @@ def build_simulation_page_html(
|
||||
)
|
||||
|
||||
cards = _summary_cards_html(
|
||||
close_val, bb_txt, gt_trades, gt_pnl, sim_pnl, len(capped), go_flag
|
||||
close_val,
|
||||
bb_txt,
|
||||
gt_trades,
|
||||
gt_pnl,
|
||||
sim_sized_pnl,
|
||||
sim_fixed_pnl,
|
||||
len(capped),
|
||||
go_flag,
|
||||
model_note=model_note,
|
||||
)
|
||||
|
||||
if frames is not None:
|
||||
@@ -415,6 +446,7 @@ def build_simulation_page_html(
|
||||
note=note,
|
||||
truth_trades=gt_trades,
|
||||
sim_trades=_fires_to_chart_trades(holdout_fires),
|
||||
# 차트 마커는 holdout 전체; 카드·테이블은 일한도 capped
|
||||
title_suffix="1단계 시뮬레이션 (monitor · holdout)",
|
||||
legend_html=legend,
|
||||
footer_sections=sections,
|
||||
|
||||
Reference in New Issue
Block a user