변경 사항 (Changes): 구문 오류(Syntax error) 및 토큰 낭비를 방지하기 위해 에이전트 쉘(Agent shell)과 파이썬 코드 스니펫에 다수의 신규 안전 규칙(Safety rules)을 추가함. 스키마 검증 및 적절한 SQL 포맷팅을 보장하기 위해 임시(Ad-hoc) 데이터베이스 쿼리 작성 가이드라인을 도입함. 코드 수정 후 UI 기능이 정상 작동하는지 확인하기 위해, 백테스트 웹 서비스 재시작 및 브라우저 검증에 대한 새로운 규칙을 구현함. 시스템 전반의 무결성(Integrity)을 유지하기 위해 실전 매매(Live trading), 웹 백테스팅, 파라미터 탐색(Parameter searches) 간의 일관성 검사(Consistency checks) 체계를 확립함. 기대 효과 (Impact): 이러한 개선 사항들은 트레이딩 시스템의 견고성(Robustness)과 신뢰성을 향상시키며, 에러 발생을 최소화하고 다양한 시스템 컴포넌트 간의 원활한 상호작용을 보장함.
127 lines
4.6 KiB
Python
127 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""7/9 SHORT 종목일일손익게이트 백테 검증 — Case A(edge=2000) vs B(edge=0)."""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import traceback
|
|
from collections import defaultdict
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from database import TradeDB
|
|
from kis_trader.engine import tail_engine as te
|
|
from kis_trader.backtest import tail_backtest_common as tbc
|
|
from kis_trader.engine.tail_tick_replay import tail_backtest_wants_tick_replay
|
|
|
|
|
|
def _summarize(trades, label: str) -> dict:
|
|
total = len(trades)
|
|
wins = [t for t in trades if float(t.get("pnl") or 0) > 0]
|
|
pnl = sum(float(t.get("pnl") or 0) for t in trades)
|
|
by_code: dict = defaultdict(list)
|
|
for t in trades:
|
|
by_code[t.get("code")].append(t)
|
|
multi = sum(1 for v in by_code.values() if len(v) > 1)
|
|
wr = (len(wins) / total * 100) if total else 0.0
|
|
print(
|
|
f" trades={total} wins={len(wins)} WR={wr:.1f}% "
|
|
f"pnl={pnl:+,.0f} multi_codes={multi}",
|
|
flush=True,
|
|
)
|
|
for code, ts in sorted(by_code.items()):
|
|
cum = 0.0
|
|
parts = []
|
|
for t in ts:
|
|
cum += float(t.get("pnl") or 0)
|
|
hm = str(t.get("entry_time") or t.get("candle_time") or "")[8:12]
|
|
parts.append(
|
|
f"{hm}:{t.get('pnl', 0):+.0f}({t.get('exit_reason', '')})→cum{cum:+.0f}"
|
|
)
|
|
if len(ts) > 1:
|
|
print(f" [{code}] " + " | ".join(parts), flush=True)
|
|
return {"label": label, "trades": total, "pnl": pnl, "multi": multi}
|
|
|
|
|
|
def main() -> int:
|
|
print("=== 7/9 SHORT 게이트 백테 검증 (skip_hts=DB) ===", flush=True)
|
|
db = TradeDB()
|
|
start, end = "2026-07-09", "2026-07-09"
|
|
start_key = start.replace("-", "") + "0000"
|
|
end_key = end.replace("-", "") + "2359"
|
|
start_ymd, end_ymd = start_key[:8], end_key[:8]
|
|
|
|
try:
|
|
base = te.get_tail_defaults_from_db(db)
|
|
skip_hts = bool(base.get("skip_hts_scan_dupes"))
|
|
print(f"skip_hts_scan_dupes(DB)={skip_hts}", flush=True)
|
|
|
|
universe, src, n_slots, _ = tbc.resolve_tail_universe(
|
|
start_ymd, end_ymd, use_saved_history=True, strategy_id="SHORT",
|
|
)
|
|
tf = int(base.get("timeframe") or 3)
|
|
rsi = int(base.get("rsi_period") or 14)
|
|
candles_by_code, _, _ = tbc.load_tail_candles_by_code(
|
|
db, start_key, end_key, tf, rsi_period=rsi,
|
|
)
|
|
codes = len(candles_by_code)
|
|
bars = sum(len(v) for v in candles_by_code.values())
|
|
slot = float(base.get("slot_money") or 300000)
|
|
budget = float(base.get("total_budget_krw") or slot * int(base.get("max_stocks") or 4))
|
|
print(
|
|
f"[로드] universe={src} slots={n_slots} codes={codes} bars={bars} "
|
|
f"slot={slot} budget={budget}",
|
|
flush=True,
|
|
)
|
|
|
|
fee = float(base.get("fee_rate") or 0.00015)
|
|
tax = float(base.get("sell_tax") or 0.0023)
|
|
meta = {"db": db, "start_key": start_key, "end_key": end_key}
|
|
use_tick = tail_backtest_wants_tick_replay(base)
|
|
print(f"[로드] tick_replay={use_tick}", flush=True)
|
|
|
|
cases = [
|
|
("A GATE_ON edge=2000", 30000.0, 1.5, 2000.0),
|
|
("B GATE_ON edge=0", 30000.0, 1.5, 0.0),
|
|
]
|
|
results = []
|
|
for label, krw, pct, edge in cases:
|
|
params = dict(base)
|
|
params["symbol_daily_loss_limit_krw"] = krw
|
|
params["symbol_daily_loss_limit_pct"] = pct
|
|
params["reentry_min_edge_krw"] = edge
|
|
params["skip_hts_scan_dupes"] = skip_hts
|
|
print(
|
|
f"\n--- {label}: krw={krw} pct={pct} edge={edge} ---",
|
|
flush=True,
|
|
)
|
|
trades = tbc.run_tail_backtest_web_aligned(
|
|
candles_by_code,
|
|
params,
|
|
universe,
|
|
slot_money=slot,
|
|
fee_rate=fee,
|
|
sell_tax=tax,
|
|
total_budget_krw=budget,
|
|
meta_out=meta,
|
|
)
|
|
results.append(_summarize(trades, label))
|
|
|
|
print("\n======== SUMMARY ========", flush=True)
|
|
for r in results:
|
|
print(f" {r['label']}: trades={r['trades']} pnl={r['pnl']:+,.0f} multi={r['multi']}", flush=True)
|
|
print("\n[LIVE 7/9] trades=3(신규2) pnl=-7,053 (376980 전일포지션 포함)", flush=True)
|
|
print("✅ VERIFY DONE", flush=True)
|
|
return 0
|
|
except Exception:
|
|
traceback.print_exc()
|
|
return 1
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|