GT 총자산 비율 매수·leg 티어 배분과 시뮬/실거래 포지션 사이징을 통합한다.
타점·비중을 gt_model로 일반화하고, amount_krw 시각순 배분·EV/WF·상위 leg 대형 매수를 position_sizing과 시뮬 HTML(고정 ₩/회 비교)에 반영한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -28,6 +28,8 @@ from config import (
|
||||
GT_INITIAL_CASH_KRW,
|
||||
GT_MARKER_SIZE_MAX,
|
||||
GT_MARKER_SIZE_MIN,
|
||||
GT_MAX_BUY_ORDER_KRW,
|
||||
LIVE_ORDER_KRW,
|
||||
MACD_FAST,
|
||||
MACD_SIGNAL,
|
||||
MACD_SLOW,
|
||||
@@ -58,16 +60,94 @@ def interval_chart_label(interval_min: int) -> str:
|
||||
return f"{interval_min}분봉"
|
||||
|
||||
|
||||
def _marker_sizes(trades: list[dict], action: str) -> list[float]:
|
||||
"""비중(weight, 0~1)에 비례한 삼각형 크기."""
|
||||
pts = [t for t in trades if t.get("action") == action]
|
||||
def _marker_hover_text(
|
||||
label: str,
|
||||
t: dict,
|
||||
*,
|
||||
default_order_krw: float | None = None,
|
||||
extra_lines: list[str] | None = None,
|
||||
) -> str:
|
||||
"""
|
||||
차트 마커 툴팁: 체결가(price)와 체결 원화(amount_krw)를 함께 표시.
|
||||
|
||||
Args:
|
||||
label: 정답/시뮬 매수·매도 라벨.
|
||||
t: trade dict (price, amount_krw, weight, memo …).
|
||||
default_order_krw: amount_krw 없을 때 표시할 기본 원화(시뮬 고정 주문).
|
||||
extra_lines: 툴팁 하단 추가 줄.
|
||||
|
||||
Returns:
|
||||
hovertext HTML 줄바꿈 문자열.
|
||||
"""
|
||||
action = t.get("action", t.get("side", ""))
|
||||
amt_label = "매수금액" if action == "buy" else "매도금액"
|
||||
lines = [
|
||||
label,
|
||||
str(t.get("dt", ""))[:16],
|
||||
f"체결가 ₩{float(t['price']):,.0f}",
|
||||
]
|
||||
ak = t.get("amount_krw")
|
||||
if ak is not None and float(ak) > 0:
|
||||
lines.append(f"{amt_label} ₩{float(ak):,.0f}")
|
||||
elif default_order_krw is not None and action == "buy":
|
||||
lines.append(f"{amt_label} ₩{float(default_order_krw):,.0f}")
|
||||
else:
|
||||
lines.append(f"{amt_label} (미배분)")
|
||||
if t.get("weight") is not None:
|
||||
lines.append(f"비중 {float(t.get('weight', 1)) * 100:.0f}%")
|
||||
if extra_lines:
|
||||
lines.extend(extra_lines)
|
||||
memo = t.get("memo", "")
|
||||
if memo:
|
||||
lines.append(str(memo))
|
||||
rule_id = t.get("rule_id", "")
|
||||
if rule_id:
|
||||
lines.append(str(rule_id))
|
||||
return "<br>".join(lines)
|
||||
|
||||
|
||||
def _trade_amount_krw(t: dict) -> float:
|
||||
"""
|
||||
마커 크기·툴팁용 체결 원화. amount_krw 없으면 비중×상한으로 추정.
|
||||
|
||||
Args:
|
||||
t: trade dict.
|
||||
|
||||
Returns:
|
||||
원화 금액(0 이상).
|
||||
"""
|
||||
ak = t.get("amount_krw")
|
||||
if ak is not None and float(ak) > 0:
|
||||
return float(ak)
|
||||
return max(float(t.get("weight", 1.0)), 0.05) * float(GT_MAX_BUY_ORDER_KRW)
|
||||
|
||||
|
||||
def _marker_sizes(pts: list[dict]) -> list[float]:
|
||||
"""
|
||||
체결 원화(amount_krw)에 비례한 삼각형 크기.
|
||||
|
||||
같은 trace(매수 또는 매도) 안에서 최소·최대 금액으로 선형 스케일.
|
||||
|
||||
Args:
|
||||
pts: 동일 action의 trade dict 리스트.
|
||||
|
||||
Returns:
|
||||
plotly marker size(diameter) 리스트.
|
||||
"""
|
||||
if not pts:
|
||||
return []
|
||||
lo, hi = float(GT_MARKER_SIZE_MIN), float(GT_MARKER_SIZE_MAX)
|
||||
return [
|
||||
lo + (hi - lo) * min(max(float(t.get("weight", 1.0)), 0.05), 1.0)
|
||||
for t in pts
|
||||
]
|
||||
amounts = [_trade_amount_krw(t) for t in pts]
|
||||
amin, amax = min(amounts), max(amounts)
|
||||
sizes: list[float] = []
|
||||
for amount in amounts:
|
||||
if amax > amin:
|
||||
ratio = (amount - amin) / (amax - amin)
|
||||
else:
|
||||
ratio = 0.5
|
||||
ratio = max(ratio, 0.08)
|
||||
sizes.append(lo + (hi - lo) * ratio)
|
||||
return sizes
|
||||
|
||||
|
||||
def _add_sim_markers(fig, trades: list[dict], row: int = 1) -> None:
|
||||
@@ -101,9 +181,14 @@ def _add_sim_markers(fig, trades: list[dict], row: int = 1) -> None:
|
||||
opacity=0.75,
|
||||
),
|
||||
hovertext=[
|
||||
f"{label}<br>{t['dt'][:16]}<br>₩{float(t['price']):,.0f}"
|
||||
f"<br>leg_gt {float(t.get('forward_ret_pct', 0)):+.2f}%"
|
||||
f"<br>{t.get('rule_id', '')}"
|
||||
_marker_hover_text(
|
||||
label,
|
||||
t,
|
||||
default_order_krw=LIVE_ORDER_KRW,
|
||||
extra_lines=[
|
||||
f"leg_gt {float(t.get('forward_ret_pct', 0)):+.2f}%",
|
||||
],
|
||||
)
|
||||
for t in pts
|
||||
],
|
||||
hovertemplate="%{hovertext}<extra></extra>",
|
||||
@@ -114,7 +199,7 @@ def _add_sim_markers(fig, trades: list[dict], row: int = 1) -> None:
|
||||
|
||||
|
||||
def _add_truth_markers(fig, trades: list[dict], row: int = 1) -> None:
|
||||
"""정답 매수·매도 마커 (삼각형 크기 = 비중)."""
|
||||
"""정답 매수·매도 마커 (삼각형 크기 = 체결 원화 금액)."""
|
||||
for action, color, symbol, label in [
|
||||
("buy", "#16a34a", "triangle-up", "정답 매수"),
|
||||
("sell", "#dc2626", "triangle-down", "정답 매도"),
|
||||
@@ -122,7 +207,7 @@ def _add_truth_markers(fig, trades: list[dict], row: int = 1) -> None:
|
||||
pts = [t for t in trades if t.get("action") == action]
|
||||
if not pts:
|
||||
continue
|
||||
sizes = _marker_sizes(trades, action)
|
||||
sizes = _marker_sizes(pts)
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=[pd.Timestamp(t["dt"]) for t in pts],
|
||||
@@ -138,9 +223,7 @@ def _add_truth_markers(fig, trades: list[dict], row: int = 1) -> None:
|
||||
line=dict(width=1.5, color="#111"),
|
||||
),
|
||||
hovertext=[
|
||||
f"{label}<br>{t['dt'][:16]}<br>₩{t['price']:,.0f}"
|
||||
f"<br>비중 {float(t.get('weight', 1))*100:.0f}%"
|
||||
f"<br>{t.get('memo', '')}"
|
||||
_marker_hover_text(label, t)
|
||||
for t in pts
|
||||
],
|
||||
hovertemplate="%{hovertext}<extra></extra>",
|
||||
@@ -446,7 +529,7 @@ def build_chart_html(
|
||||
)
|
||||
trade_table = f"""
|
||||
<h2>정답 타점 (ground_truth)</h2>
|
||||
<p class="meta">삼각형 크기 = 비중. 매수: 저점 분할 / 매도: 고점 1~2회.
|
||||
<p class="meta">삼각형 크기 = 체결 금액(매수/매도 각각 min~max). 매수: 저점 분할 / 매도: 고점 1~2회.
|
||||
총평가 = 체결 직후 현금 + 보유×체결가.{mark_note}</p>
|
||||
<table>
|
||||
<thead><tr><th>시각</th><th>구분</th><th>비중</th><th>가격</th><th>총 평가금액</th><th>해석</th></tr></thead>
|
||||
@@ -471,8 +554,11 @@ def build_chart_html(
|
||||
)
|
||||
|
||||
default_legend = (
|
||||
"▲ <b>정답 매수</b> · ▼ <b>정답 매도</b> — 삼각형 크기 = 비중.<br>"
|
||||
"● <b>시뮬 매수</b> · ● <b>시뮬 매도</b> — 원 = monitor_rules holdout 발화."
|
||||
"▲ <b>정답 매수</b> · ▼ <b>정답 매도</b> — 크기=체결금액. "
|
||||
"매수=총자산×비중×leg티어(상위 대형). "
|
||||
"툴팁: 체결가·매수/매도금액.<br>"
|
||||
"● <b>시뮬 매수</b> · ● <b>시뮬 매도</b> — 원 = holdout 발화 "
|
||||
f"(매수 ₩{LIVE_ORDER_KRW:,.0f}/회)."
|
||||
)
|
||||
if cards_html:
|
||||
cards_inner = cards_html
|
||||
|
||||
Reference in New Issue
Block a user