feat: 틱/호가 데이터 출처 표출 UI 추가 및 데이터 수집 개선
- 백테스트 및 실거래 시 틱과 호가의 벤더 출처(ob_source, entry_source) 기록 및 추적 강화 (tail_engine.py) - 웹 UI '체결디버그'에 [틱:kis / 호가:ls] 형태로 데이터 출처를 직관적으로 표출 (backtest.js, backtest.html) - LS WebSocket 구독 100건 제한 하드코딩 해제 및 env_config_ext 연동 (ls_ws.py) - 기타 백테스트 웹 및 DB 관련 최적화 적용
This commit is contained in:
@@ -1168,15 +1168,41 @@ class TradingOrchestrator:
|
||||
# 1) LS 전용 테이블 — 호가와 동일: 구독 중이면 적재 (영구만이 아님).
|
||||
# 봉·VI 는 아래 _ls_should_persist(영구) 유지.
|
||||
if get_env_bool("LS_WS_TICK_SAVE", False) and self._ls_is_subscribed(code):
|
||||
tk_str = None
|
||||
lag_sec = None
|
||||
ts_dt = payload.get("ts")
|
||||
che = str(payload.get("chetime") or "").strip()
|
||||
if isinstance(ts_dt, datetime.datetime) and che and len(che) == 6:
|
||||
tk_str = ts_dt.strftime("%Y%m%d") + che
|
||||
try:
|
||||
che_dt = datetime.datetime.strptime(tk_str, "%Y%m%d%H%M%S")
|
||||
lag_sec = (ts_dt - che_dt).total_seconds()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self.db.insert_ls_ws_tick(
|
||||
code=code,
|
||||
ts=payload.get("ts"),
|
||||
ts=ts_dt,
|
||||
price=float(payload.get("price") or 0),
|
||||
volume=payload.get("volume"),
|
||||
tot_volume=payload.get("tot_volume"),
|
||||
chetime=str(payload.get("chetime") or ""),
|
||||
chetime=che,
|
||||
tr_cd=str(payload.get("tr_cd") or ""),
|
||||
tk=tk_str,
|
||||
lag_sec=lag_sec,
|
||||
)
|
||||
# 가끔 오래된 행 정리 (쓰로틀 — 매 틱마다 DELETE 금지)
|
||||
try:
|
||||
keep_lt = int(get_env_int("LS_WS_TICK_KEEP_DAYS", 7) or 7)
|
||||
if keep_lt > 0 and hasattr(self.db, "cleanup_old_ls_ws_ticks"):
|
||||
now_m = time.monotonic()
|
||||
last = float(getattr(self, "_ls_tick_cleanup_mono", 0.0) or 0.0)
|
||||
if now_m - last >= 3600.0:
|
||||
self._ls_tick_cleanup_mono = now_m
|
||||
self.db.cleanup_old_ls_ws_ticks(keep_days=keep_lt)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 1b) 호가 틱동기 — 체결 1건당 RAM 호가 1장 (스냅 없으면 생략=실매와 동일)
|
||||
try:
|
||||
self._maybe_save_ls_orderbook_on_tick(code, payload)
|
||||
@@ -1206,7 +1232,24 @@ class TradingOrchestrator:
|
||||
|
||||
def _on_orderbook(code: str, snap: dict) -> None:
|
||||
if self._ls_should_persist_orderbook(code):
|
||||
self.db.insert_ls_ws_orderbook(code=code, snap=snap, market="KR")
|
||||
tk_str = None
|
||||
lag_sec = None
|
||||
snap_time = str(snap.get("snap_time") or "").strip()
|
||||
if len(snap_time) >= 14:
|
||||
tk_str = snap_time[:14]
|
||||
try:
|
||||
tk_dt = datetime.datetime.strptime(tk_str, "%Y%m%d%H%M%S")
|
||||
recv_ts_raw = snap.get("recv_ts")
|
||||
if isinstance(recv_ts_raw, datetime.datetime):
|
||||
recv_dt = recv_ts_raw
|
||||
elif isinstance(recv_ts_raw, float):
|
||||
recv_dt = datetime.datetime.fromtimestamp(recv_ts_raw)
|
||||
else:
|
||||
recv_dt = datetime.datetime.now()
|
||||
lag_sec = (recv_dt - tk_dt).total_seconds()
|
||||
except Exception:
|
||||
pass
|
||||
self.db.insert_ls_ws_orderbook(code=code, snap=snap, market="KR", tk=tk_str, lag_sec=lag_sec)
|
||||
|
||||
def _on_vi(code: str, payload: dict) -> None:
|
||||
if not self._ls_should_persist(code):
|
||||
|
||||
Reference in New Issue
Block a user