refactor: enhance Optuna backtesting framework, optimize orderbook filtering, and update database management utilities.
This commit is contained in:
@@ -294,7 +294,14 @@ class KiwoomWebSocketPriceCache:
|
||||
return max(0.0, min(1.0, get_env_float("KIWOOM_WS_REG_DEBOUNCE_SEC", 0.12)))
|
||||
|
||||
def _orderbook_ws_enabled(self) -> bool:
|
||||
return get_env_bool("KIWOOM_WS_ORDERBOOK_ENABLED", True)
|
||||
# 사용자의 혼동 방지를 위해 KIWOOM_WS_ORDERBOOK_ENABLED 대신,
|
||||
# LIVE_OB_PROVIDER가 kiwoom이거나, 키움 호가 적재(WS_ORDERBOOK_SAVE_KIWOOM)가 켜져있으면 자동 수신.
|
||||
from ..utils.env import get_env_from_db, get_env_bool
|
||||
live_ob_provider = (get_env_from_db("LIVE_OB_PROVIDER", "kiwoom") or "kiwoom").strip().lower()
|
||||
save_kiwoom = get_env_bool("WS_ORDERBOOK_SAVE_KIWOOM", True)
|
||||
if live_ob_provider == "kiwoom" or save_kiwoom:
|
||||
return True
|
||||
return get_env_bool("KIWOOM_WS_ORDERBOOK_ENABLED", False)
|
||||
|
||||
def _program_ws_enabled(self) -> bool:
|
||||
return get_env_bool("KIWOOM_WS_PROGRAM_ENABLED", True)
|
||||
@@ -594,6 +601,17 @@ class KiwoomWebSocketPriceCache:
|
||||
token = self._get_kiwoom_token()
|
||||
if not token:
|
||||
logger.warning("⚠️ 키움 토큰 발급 실패 → WS 연결 보류 (60s)")
|
||||
try:
|
||||
from kis_trader.utils.ops_alert import ops_alert
|
||||
ops_alert(
|
||||
"token_kiwoom",
|
||||
"키움 토큰 발급 실패",
|
||||
detail="시세 WS·조건검색 불가 → 60s 후 재시도",
|
||||
level="critical",
|
||||
session_only=False,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(60)
|
||||
return
|
||||
|
||||
@@ -666,6 +684,17 @@ class KiwoomWebSocketPriceCache:
|
||||
self._fire_login_callbacks(ws)
|
||||
else:
|
||||
logger.warning("❌ 키움 WS LOGIN 실패 rc=%s msg=%s", rc, rm)
|
||||
try:
|
||||
from kis_trader.utils.ops_alert import ops_alert
|
||||
ops_alert(
|
||||
"ws_kiwoom_login",
|
||||
"키움 WS LOGIN 실패",
|
||||
detail=f"rc={rc} msg={rm}",
|
||||
level="critical",
|
||||
session_only=False,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
# 805004 등 서버 토큰 거부 → 캐시 만료 전이라도 무효화 후 재발급
|
||||
if self._is_token_rejected_login(rc, rm):
|
||||
self._invalidate_token_cache(
|
||||
@@ -775,7 +804,7 @@ class KiwoomWebSocketPriceCache:
|
||||
filt = self._candle_agg_codes
|
||||
if filt is None or code in filt:
|
||||
try:
|
||||
self._candle_agg.on_tick(code, price, tick_vol, tick_time)
|
||||
self._candle_agg.on_tick(code, price, tick_vol, tick_time, source="kiwoom")
|
||||
except Exception as ex:
|
||||
logger.debug("키움→CandleAggregator on_tick 실패 %s: %s", code, ex)
|
||||
|
||||
@@ -787,6 +816,25 @@ class KiwoomWebSocketPriceCache:
|
||||
)
|
||||
except Exception as ex:
|
||||
logger.debug("키움→TickRecorder on_tick 실패 %s: %s", code, ex)
|
||||
|
||||
# ── 호가 틱동기: 체결 1건당 RAM 0D 스냅 1장 (LS tick 모드와 동일) ──
|
||||
if self._trigger_snapshot_recorder is not None:
|
||||
try:
|
||||
max_age = float(
|
||||
getattr(
|
||||
self._trigger_snapshot_recorder,
|
||||
"orderbook_tick_max_age_sec",
|
||||
3.0,
|
||||
)
|
||||
or 3.0
|
||||
)
|
||||
snap = self.get_orderbook_snapshot(code, max_age_sec=max_age)
|
||||
if snap is not None:
|
||||
self._trigger_snapshot_recorder.on_orderbook_tick_sync(
|
||||
code, snap, snap_time=tick_time,
|
||||
)
|
||||
except Exception as ex:
|
||||
logger.debug("키움→호가틱동기 실패 %s: %s", code, ex)
|
||||
except (ValueError, TypeError) as e:
|
||||
logger.debug("키움 0B 파싱 오류 %s: %s", code, e)
|
||||
|
||||
@@ -818,7 +866,7 @@ class KiwoomWebSocketPriceCache:
|
||||
|
||||
def _send_reg(self, codes: list) -> bool:
|
||||
"""REG 발송 (그룹 1, 0B+0D 타입). 한 번에 여러 종목 OK."""
|
||||
if not codes or not self._ws:
|
||||
if not codes or not self._ws or not self._connected:
|
||||
return False
|
||||
try:
|
||||
self._ws.send(json.dumps({
|
||||
@@ -839,7 +887,7 @@ class KiwoomWebSocketPriceCache:
|
||||
|
||||
def _send_remove(self, codes: list) -> bool:
|
||||
"""REMOVE 발송."""
|
||||
if not codes or not self._ws:
|
||||
if not codes or not self._ws or not self._connected:
|
||||
return False
|
||||
try:
|
||||
self._ws.send(json.dumps({
|
||||
|
||||
Reference in New Issue
Block a user