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:
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
40만 원 기준 매수·매도 최종 리허설 (DB 없이 synthetic + paper replay).
|
||||
40만 원 기준 매수·매도 리허설 (DB 없이 synthetic + hybrid replay).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -13,14 +13,13 @@ runpy.run_path(str(Path(__file__).resolve().parent / "_bootstrap.py"))
|
||||
import pandas as pd
|
||||
|
||||
from config import GT_INITIAL_CASH_KRW, TRADING_FEE_RATE
|
||||
from deepcoin.matching.position_sizing import load_ev_wf_approved_rule_ids
|
||||
from deepcoin.ops.hybrid_sim_execution import (
|
||||
HybridSimPortfolio,
|
||||
hit_key,
|
||||
plan_live_hit,
|
||||
replay_paper_portfolio,
|
||||
replay_hybrid_signals,
|
||||
sort_hits_sim_order,
|
||||
)
|
||||
from deepcoin.ops.paper_portfolio import PaperPortfolio
|
||||
|
||||
|
||||
def _mini_ohlc() -> pd.DataFrame:
|
||||
@@ -47,7 +46,7 @@ def test_sort_buy_before_sell() -> None:
|
||||
def test_sell_without_holdings() -> None:
|
||||
"""보유 없이 매도만 → 모의 보유 없음."""
|
||||
ohlc = _mini_ohlc()
|
||||
approved = load_ev_wf_approved_rule_ids()
|
||||
approved = None
|
||||
hist = [
|
||||
{
|
||||
"dt": "2026-06-01 12:00:00",
|
||||
@@ -56,18 +55,18 @@ def test_sell_without_holdings() -> None:
|
||||
"close": 500.0,
|
||||
}
|
||||
]
|
||||
paper, results = replay_paper_portfolio(hist, ohlc, approved_buy_rules=approved)
|
||||
portfolio, results = replay_hybrid_signals(hist, ohlc, approved_buy_rules=approved)
|
||||
key = hit_key(hist[0])
|
||||
res = results[key]
|
||||
assert not res.ok and "보유 없음" in res.message, res
|
||||
assert paper.qty < 1e-9 and paper.cash_krw == float(GT_INITIAL_CASH_KRW)
|
||||
assert portfolio.qty < 1e-9 and portfolio.cash_krw == float(GT_INITIAL_CASH_KRW)
|
||||
print(" [OK] 보유 없음 매도 스킵")
|
||||
|
||||
|
||||
def test_buy_then_partial_sell() -> None:
|
||||
"""매수 후 분할 매도 1회."""
|
||||
ohlc = _mini_ohlc()
|
||||
approved = load_ev_wf_approved_rule_ids()
|
||||
approved = None
|
||||
dt_buy = str(ohlc.index[50])
|
||||
dt_sell = str(ohlc.index[80])
|
||||
price_buy = float(ohlc.loc[ohlc.index[50], "Close"])
|
||||
@@ -86,22 +85,22 @@ def test_buy_then_partial_sell() -> None:
|
||||
"close": price_sell,
|
||||
},
|
||||
]
|
||||
paper, results = replay_paper_portfolio(hist, ohlc, approved_buy_rules=approved)
|
||||
portfolio, results = replay_hybrid_signals(hist, ohlc, approved_buy_rules=approved)
|
||||
buy_res = results[hit_key(hist[0])]
|
||||
sell_res = results[hit_key(hist[1])]
|
||||
assert buy_res.ok, buy_res.message
|
||||
assert paper.qty > 0 or sell_res.ok, (paper.qty, sell_res)
|
||||
assert portfolio.qty > 0 or sell_res.ok, (portfolio.qty, sell_res)
|
||||
if sell_res.ok:
|
||||
assert sell_res.sell_qty > 0 and sell_res.amount_krw > 0
|
||||
assert paper.cash_krw > float(GT_INITIAL_CASH_KRW) * 0.5
|
||||
assert portfolio.cash_krw > float(GT_INITIAL_CASH_KRW) * 0.5
|
||||
print(
|
||||
f" [OK] 매수 ₩{buy_res.amount_krw:,.0f} → 매도 "
|
||||
f"ok={sell_res.ok} qty={sell_res.sell_qty:.4f} 현금=₩{paper.cash_krw:,.0f}"
|
||||
f"ok={sell_res.ok} qty={sell_res.sell_qty:.4f} 현금=₩{portfolio.cash_krw:,.0f}"
|
||||
)
|
||||
|
||||
|
||||
def test_unapproved_buy_excluded_from_sizing() -> None:
|
||||
"""EV/WF 미포함 매수는 hybrid 배분 입력에서 제외."""
|
||||
def test_unapproved_buy_excluded_when_filter_set() -> None:
|
||||
"""approved_buy_rules 지정 시에만 미포함 매수 제외 (시뮬 기본은 필터 없음)."""
|
||||
ohlc = _mini_ohlc()
|
||||
hist = [
|
||||
{
|
||||
@@ -112,15 +111,15 @@ def test_unapproved_buy_excluded_from_sizing() -> None:
|
||||
},
|
||||
]
|
||||
approved = {"buy_compound_tight"}
|
||||
sized_hist = replay_paper_portfolio(hist, ohlc, approved_buy_rules=approved)[0]
|
||||
sized_hist = replay_hybrid_signals(hist, ohlc, approved_buy_rules=approved)[0]
|
||||
assert sized_hist.qty < 1e-9
|
||||
print(" [OK] 미승인 매수 규칙 → 체결 없음")
|
||||
print(" [OK] approved_buy_rules 지정 시 미승인 매수 제외")
|
||||
|
||||
|
||||
def test_plan_live_matches_replay() -> None:
|
||||
"""plan_live_hit == replay 마지막 건."""
|
||||
ohlc = _mini_ohlc()
|
||||
approved = load_ev_wf_approved_rule_ids()
|
||||
approved = None
|
||||
hist = []
|
||||
hit = {
|
||||
"dt": str(ohlc.index[70]),
|
||||
@@ -130,7 +129,7 @@ def test_plan_live_matches_replay() -> None:
|
||||
}
|
||||
plan = plan_live_hit(hist, hit, ohlc, approved_buy_rules=approved)
|
||||
hist.append(hit)
|
||||
_, results = replay_paper_portfolio(hist, ohlc, approved_buy_rules=approved)
|
||||
_, results = replay_hybrid_signals(hist, ohlc, approved_buy_rules=approved)
|
||||
replay_res = results[hit_key(hit)]
|
||||
assert plan.amount_krw == replay_res.amount_krw, (plan, replay_res)
|
||||
assert plan.ok == replay_res.ok
|
||||
@@ -140,7 +139,7 @@ def test_plan_live_matches_replay() -> None:
|
||||
def test_initial_cash_400k_large_buy() -> None:
|
||||
"""40만·대형 DD 시 매수액 ≤ 가용현금."""
|
||||
ohlc = _mini_ohlc()
|
||||
approved = load_ev_wf_approved_rule_ids()
|
||||
approved = None
|
||||
hit = {
|
||||
"dt": str(ohlc.index[100]),
|
||||
"rule_id": "buy_compound_tight",
|
||||
@@ -155,9 +154,9 @@ def test_initial_cash_400k_large_buy() -> None:
|
||||
print(f" [OK] 40만 대형 tier 매수 ₩{plan.amount_krw:,.0f} (≤{GT_INITIAL_CASH_KRW:,})")
|
||||
|
||||
|
||||
def test_paper_apply_buy_insufficient() -> None:
|
||||
def test_apply_buy_insufficient_cash() -> None:
|
||||
"""현금 부족 시 apply_buy 실패."""
|
||||
p = PaperPortfolio()
|
||||
p = HybridSimPortfolio()
|
||||
p.cash_krw = 10_000.0
|
||||
ok = p.apply_buy(50_000, 500.0, leg_id=1)
|
||||
assert not ok
|
||||
@@ -167,16 +166,16 @@ def test_paper_apply_buy_insufficient() -> None:
|
||||
def main() -> int:
|
||||
"""리허설 실행."""
|
||||
print(f"[리허설] GT_INITIAL_CASH_KRW=₩{GT_INITIAL_CASH_KRW:,}")
|
||||
print(f" approved buys: {load_ev_wf_approved_rule_ids()}")
|
||||
print(" approved_buy_rules: None (sim_causal_hybrid 동일)")
|
||||
fails = 0
|
||||
tests = [
|
||||
test_sort_buy_before_sell,
|
||||
test_sell_without_holdings,
|
||||
test_buy_then_partial_sell,
|
||||
test_unapproved_buy_excluded_from_sizing,
|
||||
test_unapproved_buy_excluded_when_filter_set,
|
||||
test_plan_live_matches_replay,
|
||||
test_initial_cash_400k_large_buy,
|
||||
test_paper_apply_buy_insufficient,
|
||||
test_apply_buy_insufficient_cash,
|
||||
]
|
||||
for fn in tests:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user