Files
Bithumb/deepcoin/ground_truth/gt_allocation_analysis.py
xavis e68bb44083 인과적 GT 신호·복리 배분 시뮬을 도입하고 운영 정합성을 맞춘다.
미래 데이터를 쓰지 않는 causal 신호/tier와 전기간 복리 포트폴리오 비교로 GT 대비 sim_sized 검증 경로를 정리하고, 일한도·매수 상한·live_buy 스케일을 제거한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 19:50:54 +09:00

151 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
GT 체결 amount_krw·총자산 비율 분석 — 시뮬 tier·배분율 최적 추정.
"""
from __future__ import annotations
from typing import Any
from config import (
GT_BUY_PCT_LARGE_LEG,
GT_BUY_PCT_SMALL_LEG,
GT_INITIAL_CASH_KRW,
GT_LARGE_LEG_TOP_PCT,
TRADING_FEE_RATE,
)
from deepcoin.matching.position_sizing import (
leg_asset_pct_scale,
optimal_weight_share,
portfolio_totals,
top_leg_ids_by_forward_return,
)
def analyze_gt_buy_allocation(
trades: list[dict[str, Any]],
*,
initial_cash: float = GT_INITIAL_CASH_KRW,
fee_rate: float = TRADING_FEE_RATE,
) -> dict[str, Any]:
"""
GT 시각순 체결에서 매수별 (실제투입/총자산) 비율을 분석합니다.
Args:
trades: amount_krw·weight·leg_id가 채워진 GT trade dict.
initial_cash: 시작 현금.
fee_rate: 수수료율.
Returns:
leg tier별·전체 배분 통계 및 권장 pct_large/pct_small.
"""
chron = sorted(trades, key=lambda x: x["dt"])
if not chron:
return {"note": "체결 없음"}
large_legs = top_leg_ids_by_forward_return(chron, GT_LARGE_LEG_TOP_PCT)
cash = float(initial_cash)
qty = 0.0
ratios_large: list[float] = []
ratios_small: list[float] = []
ratios_all: list[float] = []
for i, t in enumerate(chron):
price = float(t["price"])
if price <= 0:
continue
leg_id = int(t.get("leg_id", 0))
action = t.get("action", "")
if action == "buy":
w = float(t.get("weight", 1.0))
rem = sum(
float(chron[j].get("weight", 1.0))
for j in range(i, len(chron))
if int(chron[j].get("leg_id", 0)) == leg_id
and chron[j].get("action") == "buy"
)
opt = optimal_weight_share(w, rem) if rem > 0 else 1.0
total_asset, _, _ = portfolio_totals(cash, qty, price)
amount = float(t.get("amount_krw") or 0)
if total_asset > 0 and amount > 0 and opt > 0:
implied = amount / (total_asset * opt)
ratios_all.append(implied)
if leg_id in large_legs:
ratios_large.append(implied)
else:
ratios_small.append(implied)
if amount > 0:
fee = amount * fee_rate
cash -= amount + fee
qty += amount / price
elif action == "sell" and qty > 0:
gross = float(t.get("amount_krw") or qty * price)
cash += gross * (1.0 - fee_rate)
qty = 0.0
def _stats(vals: list[float]) -> dict[str, float]:
if not vals:
return {}
s = sorted(vals)
n = len(s)
return {
"count": n,
"mean": round(sum(s) / n, 4),
"median": round(s[n // 2], 4),
"p25": round(s[max(0, n // 4)], 4),
"p75": round(s[min(n - 1, 3 * n // 4)], 4),
}
st_all = _stats(ratios_all)
st_large = _stats(ratios_large)
st_small = _stats(ratios_small)
rec_large = st_large.get("median", GT_BUY_PCT_LARGE_LEG)
rec_small = st_small.get("median", GT_BUY_PCT_SMALL_LEG)
if not rec_large or rec_large <= 0:
rec_large = GT_BUY_PCT_LARGE_LEG
if not rec_small or rec_small <= 0:
rec_small = GT_BUY_PCT_SMALL_LEG
return {
"large_leg_ids": sorted(large_legs),
"large_leg_count": len(large_legs),
"config_pct_large": GT_BUY_PCT_LARGE_LEG,
"config_pct_small": GT_BUY_PCT_SMALL_LEG,
"observed_implied_scale": {
"all": st_all,
"large_leg": st_large,
"small_leg": st_small,
},
"recommended_pct_large_leg": round(rec_large, 4),
"recommended_pct_small_leg": round(rec_small, 4),
"note": (
"implied_scale = amount / (pre_buy_total_asset × weight_share); "
"시뮬 tier는 GT 분석 median 사용"
),
}
def gt_tier_scale_from_analysis(
leg_id: int,
large_legs: set[int],
analysis: dict[str, Any] | None = None,
) -> float:
"""
GT 분석 권장값 또는 config tier scale.
Args:
leg_id: leg 번호.
large_legs: 상위 leg.
analysis: analyze_gt_buy_allocation 결과.
Returns:
총자산 대비 매수 스케일 (0~1).
"""
if analysis and analysis.get("observed_implied_scale", {}).get("all"):
if leg_id in large_legs:
return float(analysis.get("recommended_pct_large_leg", GT_BUY_PCT_LARGE_LEG))
return float(analysis.get("recommended_pct_small_leg", GT_BUY_PCT_SMALL_LEG))
return leg_asset_pct_scale(leg_id, large_legs)