refactor: GT·시뮬·운영 3축 정리 및 hybrid 실거래 정합

Phase C/dry-run·미사용 모듈·재생성 HTML을 제거하고, 운영 체결을
sim_causal_hybrid와 동일한 hybrid 로직으로 통합한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
xavis
2026-06-03 23:50:28 +09:00
parent a16c942be4
commit d7848df6f7
85 changed files with 177180 additions and 196131 deletions

View File

@@ -217,72 +217,6 @@ def nearest_gt_leg_id(
return best_buy if best_buy is not None else best_any
_APPROVED_RULES_CACHE: set[str] | None = None
def load_ev_wf_approved_rule_ids(
matched_path: Path | None = None,
outcomes_path: Path | None = None,
) -> set[str]:
"""
holdout EV·PF, walk-forward, 수수료 스트레스를 모두 통과한 rule_id.
Args:
matched_path: matched_rules.json.
outcomes_path: fire_outcomes.csv.
Returns:
통과 rule_id set. 산출 불가 시 monitor_rules 전체 fallback.
"""
global _APPROVED_RULES_CACHE
if _APPROVED_RULES_CACHE is not None:
return set(_APPROVED_RULES_CACHE)
from config import SIM_FEE_STRESS_MULT
from deepcoin.matching.select_rules import _rule_metrics, _split_train_valid_holdout
from deepcoin.matching.simulation import (
evaluate_go_no_go,
simulate_live_order_cap,
walk_forward_by_month,
walk_forward_summary,
)
mp = matched_path or MATCHING_MATCHED_RULES
op = outcomes_path or MATCHING_FIRE_OUTCOMES
matched = load_matched_rules(mp)
rules = matched.get("monitor_rules") or []
if not rules or not op.is_file():
return {r["rule_id"] for r in rules}
import pandas as pd
from config import MATCH_FEE_RATE
outcomes = pd.read_csv(op)
outcomes["split"] = _split_train_valid_holdout(outcomes)
wf_sum = walk_forward_summary(walk_forward_by_month(outcomes))
fee_stress: dict[str, Any] = {}
for rid in outcomes["rule_id"].unique():
sub = outcomes[outcomes["rule_id"] == rid]
from deepcoin.matching.simulation import _fee_adjust_ret
adj = _fee_adjust_ret(sub["forward_ret_pct"], SIM_FEE_STRESS_MULT)
fee_stress[rid] = _rule_metrics(sub.assign(forward_ret_pct=adj))
monitor_ids = {r["rule_id"] for r in rules}
live_cap = simulate_live_order_cap(
outcomes, rule_ids=monitor_ids, holdout_only=True
)
go = evaluate_go_no_go(matched, wf_sum, fee_stress, live_cap)
passed = {c["rule_id"] for c in go.get("checks", []) if c.get("pass")}
if passed:
_APPROVED_RULES_CACHE = passed
return passed
fallback = monitor_ids
_APPROVED_RULES_CACHE = fallback
return fallback
def load_gt_allocation_analysis(
gt_trades: list[dict[str, Any]] | None = None,
) -> dict[str, Any]:
@@ -328,8 +262,6 @@ def gt_tier_scale_for_trade(
"""
GT leg tier 배분 스케일 (분석 권장값 또는 config).
시뮬은 live_buy_asset_pct_scale 대신 GT와 동일 tier 정책을 사용합니다.
Args:
trade: {dt, leg_id?, action, ...}.
gt_trades: GT trades (leg 매칭).
@@ -349,37 +281,6 @@ def gt_tier_scale_for_trade(
return gt_tier_scale_from_analysis(int(lid), large_legs, analysis)
def live_buy_asset_pct_scale(
rule_id: str,
dt: str,
gt_trades: list[dict[str, Any]],
*,
approved_rules: set[str],
large_legs: set[int],
) -> float:
"""
실거래 전용 매수 tier (EV/WF·leg 상위). 시뮬은 gt_tier_scale_for_trade 사용.
Args:
rule_id: 규칙 ID.
dt: 체결 시각.
gt_trades: GT trades.
approved_rules: 통과 rule_id.
large_legs: 상위 leg.
Returns:
LIVE_BUY_PCT_LARGE 또는 LIVE_BUY_PCT_SMALL(또는 0에 가까운 소형).
"""
from config import LIVE_BUY_PCT_LARGE, LIVE_BUY_PCT_SMALL
if rule_id not in approved_rules:
return float(LIVE_BUY_PCT_SMALL)
lid = nearest_gt_leg_id(dt, gt_trades)
if lid is not None and lid in large_legs:
return float(LIVE_BUY_PCT_LARGE)
return float(LIVE_BUY_PCT_SMALL)
def enrich_sim_trades_with_gt_weights(
trades: list[dict[str, Any]],
gt_trades: list[dict[str, Any]],
@@ -504,65 +405,6 @@ def attach_gt_model_amounts(
return enriched
def plan_open_position_buy(
open_buys: list[dict[str, Any]],
candidate: dict[str, Any],
cash: float,
qty: float,
gt_trades: list[dict[str, Any]] | None = None,
*,
large_legs: set[int],
analysis: dict[str, Any] | None = None,
fee_rate: float = TRADING_FEE_RATE,
) -> float:
"""
미청산 포지션 내 다음 매수 원화 (GT tier·보유 현금 한도, 1회 상한 없음).
Args:
open_buys: 현재 포지션에서 이미 체결된 매수 dict.
candidate: 이번 매수 후보 {dt, price, rule_id, leg_id?, ...}.
cash: 보유 현금.
qty: 보유 수량.
gt_trades: GT leg 매칭용.
large_legs: 상위 leg.
analysis: GT 배분 분석.
fee_rate: 수수료율.
Returns:
매수 계획 원화.
"""
from deepcoin.ground_truth.gt_model import leg_entry_weights
if gt_trades is None:
gt_trades, _, _ = load_sizing_context_from_gt()
if analysis is None:
analysis = load_gt_allocation_analysis(gt_trades)
prices = [float(t["price"]) for t in open_buys] + [float(candidate["price"])]
weights = leg_entry_weights(prices)
idx = len(open_buys)
w = weights[idx]
w_sum = sum(weights[idx:])
cand = dict(candidate)
if "leg_id" not in cand:
cand["leg_id"] = nearest_gt_leg_id(str(candidate["dt"]), gt_trades)
scale = gt_tier_scale_for_trade(
cand,
gt_trades,
large_legs,
analysis=analysis,
)
return compute_buy_amount_krw(
cash,
qty,
float(candidate["price"]),
w,
w_sum,
asset_pct_scale=scale,
fee_rate=fee_rate,
)
def attach_dynamic_buy_amounts(
trades: list[dict[str, Any]],
*,