refactor: enhance Optuna backtesting framework, optimize orderbook filtering, and update database management utilities.
This commit is contained in:
@@ -37,13 +37,18 @@ def _env_flag(key: str, default: bool) -> bool:
|
||||
|
||||
|
||||
def live_universe_slot_align_enabled(strategy_id: str) -> bool:
|
||||
"""실매 후보 ∩ history 슬롯 필터.
|
||||
|
||||
기본 **False** — history 지연/축소 시 전량탈락(universe_wipe) 방지.
|
||||
백테 슬롯 정합이 필요하면 ``LIVE_UNIVERSE_SLOT_ALIGN=true`` 또는 전략별 키로 ON.
|
||||
"""
|
||||
sid = (strategy_id or "").upper()
|
||||
per_key = f"{sid}_LIVE_UNIVERSE_SLOT_ALIGN"
|
||||
if get_env_from_db(per_key, "") not in (None, "", "None"):
|
||||
return _env_flag(per_key, True)
|
||||
return _env_flag(per_key, False)
|
||||
if sid in _UNIVERSE_SLOT_SKIP_DEFAULT:
|
||||
return False
|
||||
return _env_flag("LIVE_UNIVERSE_SLOT_ALIGN", True)
|
||||
return _env_flag("LIVE_UNIVERSE_SLOT_ALIGN", False)
|
||||
|
||||
|
||||
def resolve_live_universe_history_source(
|
||||
@@ -144,6 +149,59 @@ def resolve_live_min_invest_ratio(strategy_id: str) -> float:
|
||||
return min_invest_ratio_of_slot({}, strategy=portfolio_strategy_key(strategy_id))
|
||||
|
||||
|
||||
def _history_snapshot_event_time(
|
||||
db: Any,
|
||||
strategy_id: str,
|
||||
at_time: str,
|
||||
history_source: str,
|
||||
) -> Optional[Any]:
|
||||
"""``at_time`` 이전 최신 ``event_time`` (없으면 None)."""
|
||||
conn = getattr(db, "conn", None)
|
||||
if conn is None:
|
||||
return None
|
||||
table_fn = getattr(db, "_universe_history_table", None)
|
||||
if callable(table_fn):
|
||||
try:
|
||||
table = table_fn(history_source)
|
||||
except Exception:
|
||||
table = None
|
||||
else:
|
||||
table = None
|
||||
if not table:
|
||||
from kis_trader.backtest.universe_history_source import history_table_for_source
|
||||
table = history_table_for_source(history_source)
|
||||
try:
|
||||
row = conn.execute(
|
||||
f"""
|
||||
SELECT MAX(event_time) AS et
|
||||
FROM {table}
|
||||
WHERE strategy_id=%s AND event_time <= %s
|
||||
""",
|
||||
(strategy_id, at_time),
|
||||
).fetchone()
|
||||
return (row or {}).get("et") if row else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _event_time_ymd(et: Any) -> str:
|
||||
if et is None:
|
||||
return ""
|
||||
if hasattr(et, "strftime"):
|
||||
try:
|
||||
return et.strftime("%Y-%m-%d")
|
||||
except Exception:
|
||||
pass
|
||||
s = str(et).strip()
|
||||
if len(s) >= 10 and s[4] == "-" and s[7] == "-":
|
||||
return s[:10]
|
||||
# YYYYMMDDHHMM / YYYYMMDD...
|
||||
digits = "".join(ch for ch in s if ch.isdigit())
|
||||
if len(digits) >= 8:
|
||||
return f"{digits[:4]}-{digits[4:6]}-{digits[6:8]}"
|
||||
return ""
|
||||
|
||||
|
||||
def history_universe_codes_at(
|
||||
db: Any,
|
||||
strategy_id: str,
|
||||
@@ -157,6 +215,9 @@ def history_universe_codes_at(
|
||||
- kiwoom → ``target_candidates_history``
|
||||
- ls → ``ls_candidates_history``
|
||||
스냅샷 없으면 ``None`` (필터 생략 = 실시간 후보 유지).
|
||||
|
||||
**당일 스냅샷만** 사용한다. 주말 재시작·키움 매니저 다운 뒤 남은
|
||||
며칠 전 history 와 sticky 후보를 교집합하면 전원 탈락(11→0) 한다.
|
||||
"""
|
||||
when = when or dt.now()
|
||||
at_time = when.strftime("%Y-%m-%d %H:%M:%S")
|
||||
@@ -170,6 +231,14 @@ def history_universe_codes_at(
|
||||
src = resolve_live_universe_history_source(sid, universe_source=None)
|
||||
if src not in ("ls", "kiwoom"):
|
||||
src = "kiwoom"
|
||||
|
||||
# 당일(캘린더) 스냅샷만 슬롯정합에 쓴다 — 낡은 history 전멸 방지
|
||||
et = _history_snapshot_event_time(db, sid, at_time, src)
|
||||
if not et:
|
||||
return None
|
||||
if _event_time_ymd(et) != when.strftime("%Y-%m-%d"):
|
||||
return None
|
||||
|
||||
getter = getattr(db, "get_universe_at", None)
|
||||
if getter is None:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user