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:
106
kis_trader/backtest/env_timeline.py
Normal file
106
kis_trader/backtest/env_timeline.py
Normal file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
env 타임라인 — 백테스트 시 env_config / config_* 변경 시각별 스냅샷 재생.
|
||||
|
||||
실매·파람서치는 최신 env 1행만 사용. 웹 백테만 ``EnvTimeline`` 으로
|
||||
봉 시각(candle_time)마다 그 시점까지의 DB 설정을 적용한다.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from bisect import bisect_right
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
|
||||
def _created_at_to_key(created_at: str) -> str:
|
||||
"""``YYYY-MM-DD HH:MM:SS`` → ``YYYYMMDDHHMMSS``."""
|
||||
s = str(created_at or "").strip()
|
||||
if len(s) >= 19:
|
||||
return (
|
||||
s[0:4] + s[5:7] + s[8:10]
|
||||
+ s[11:13] + s[14:16] + s[17:19]
|
||||
)
|
||||
return ""
|
||||
|
||||
|
||||
def _candle_time_to_lookup_key(candle_time: str) -> str:
|
||||
"""분봉 키(YYYYMMDDHHMM) → 해당 분 마감초(…59) lookup 키."""
|
||||
t = str(candle_time or "")[:12]
|
||||
if len(t) < 12:
|
||||
return ""
|
||||
return t + "59"
|
||||
|
||||
|
||||
def _ymd_to_start_bound(ymd: str) -> str:
|
||||
y = str(ymd or "")[:8]
|
||||
if len(y) < 8:
|
||||
return ""
|
||||
return f"{y[:4]}-{y[4:6]}-{y[6:8]} 00:00:00"
|
||||
|
||||
|
||||
class EnvTimeline:
|
||||
"""env 변경 이벤트 시계열 — ``snapshot_at`` 으로 봉 시각별 merged env 조회."""
|
||||
|
||||
def __init__(self, events: List[Tuple[str, Dict[str, Any]]]) -> None:
|
||||
deduped: List[Tuple[str, Dict[str, Any]]] = []
|
||||
prev_sig: Optional[Tuple[str, ...]] = None
|
||||
for key, snap in events:
|
||||
if not key:
|
||||
continue
|
||||
sig = tuple(sorted((k, str(v)) for k, v in snap.items()))
|
||||
if sig == prev_sig:
|
||||
continue
|
||||
prev_sig = sig
|
||||
deduped.append((key, dict(snap)))
|
||||
self._keys: List[str] = [e[0] for e in deduped]
|
||||
self._snaps: List[Dict[str, Any]] = [e[1] for e in deduped]
|
||||
|
||||
@property
|
||||
def snapshot_count(self) -> int:
|
||||
return len(self._keys)
|
||||
|
||||
def snapshot_at(self, candle_time: str) -> Tuple[Dict[str, Any], str]:
|
||||
"""봉 시각 기준 적용 env 스냅샷 + 이벤트 키(YYYYMMDDHHMMSS)."""
|
||||
if not self._keys:
|
||||
return {}, ""
|
||||
qkey = _candle_time_to_lookup_key(candle_time)
|
||||
if not qkey:
|
||||
return dict(self._snaps[0]), self._keys[0]
|
||||
idx = bisect_right(self._keys, qkey) - 1
|
||||
if idx < 0:
|
||||
return {}, ""
|
||||
return dict(self._snaps[idx]), self._keys[idx]
|
||||
|
||||
|
||||
def build_env_timeline(
|
||||
db: Any,
|
||||
start_ymd: str,
|
||||
end_ymd: str,
|
||||
) -> Optional[EnvTimeline]:
|
||||
"""백테 구간 env 변경 이력 → ``EnvTimeline`` (변경 없으면 1스냅샷)."""
|
||||
if db is None:
|
||||
return None
|
||||
sy = str(start_ymd or "")[:8]
|
||||
ey = str(end_ymd or "")[:8]
|
||||
if len(sy) < 8 or len(ey) < 8:
|
||||
return None
|
||||
if not hasattr(db, "list_env_change_times_in_range"):
|
||||
return None
|
||||
if not hasattr(db, "get_merged_env_snapshot_as_of"):
|
||||
return None
|
||||
|
||||
start_bound = _ymd_to_start_bound(sy)
|
||||
event_times = db.list_env_change_times_in_range(sy, ey)
|
||||
if not event_times:
|
||||
event_times = [start_bound]
|
||||
|
||||
events: List[Tuple[str, Dict[str, Any]]] = []
|
||||
for as_of in event_times:
|
||||
merged = db.get_merged_env_snapshot_as_of(as_of)
|
||||
key = _created_at_to_key(as_of)
|
||||
if not key:
|
||||
continue
|
||||
events.append((key, merged))
|
||||
|
||||
if not events:
|
||||
return None
|
||||
return EnvTimeline(events)
|
||||
Reference in New Issue
Block a user