feat: 새로운 안전 규칙 및 최적화 적용을 통한 트레이딩 시스템 개선
변경 사항 (Changes): 구문 오류(Syntax error) 및 토큰 낭비를 방지하기 위해 에이전트 쉘(Agent shell)과 파이썬 코드 스니펫에 다수의 신규 안전 규칙(Safety rules)을 추가함. 스키마 검증 및 적절한 SQL 포맷팅을 보장하기 위해 임시(Ad-hoc) 데이터베이스 쿼리 작성 가이드라인을 도입함. 코드 수정 후 UI 기능이 정상 작동하는지 확인하기 위해, 백테스트 웹 서비스 재시작 및 브라우저 검증에 대한 새로운 규칙을 구현함. 시스템 전반의 무결성(Integrity)을 유지하기 위해 실전 매매(Live trading), 웹 백테스팅, 파라미터 탐색(Parameter searches) 간의 일관성 검사(Consistency checks) 체계를 확립함. 기대 효과 (Impact): 이러한 개선 사항들은 트레이딩 시스템의 견고성(Robustness)과 신뢰성을 향상시키며, 에러 발생을 최소화하고 다양한 시스템 컴포넌트 간의 원활한 상호작용을 보장함.
This commit is contained in:
126
scripts/tail_symbol_gate_verify_20260709.py
Normal file
126
scripts/tail_symbol_gate_verify_20260709.py
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user