Files
kis_bot/kis_trader/backtest/optuna_post_progress.py
Your Name 0ecac7cb95 이번에 들어간 내용
한투 호가 = 2번째 앱키 전용
키 없거나 start 실패 시 메인에 H0STASP0 안 붙임. 운영설정 WS_ORDERBOOK_SAVE_KIS 빨간 danger.

LS RAM 합집합
후보∪보유∪영구∪grace. sync_targets와 split reconcile 둘 다. 틱 DB 영구 게이트는 그대로.

분봉 쓰레기 → 다음 소스 봉 통째
그 분 틱 0건이거나 전부 봉끝 대비 LIVE_FEED_FALLBACK_MAX_AGE_SEC 초과면 구멍. 메인 WS → 2차 → LS → REST → rollup. CANDLE_GARBAGE_FALLBACK 기본 true.

파일: feed_fallback.py(신규), ws_manager.py, kis_ws.py, candle_series.py, bt_candle_source.py, live_config_schema.py, database.py, 스모크, MD 2개.

같은 ws_manager/database/kis_ws/live_config에는 직전 커밋 이후 쌓여 있던 시세 폴백·ENV 키 정리도 같이 들어갔습니다. 파일 단위로 나눌 수 없어서입니다.
2026-08-19 22:11:31 +09:00

138 lines
3.5 KiB
Python

"""Optuna 후처리 진행 한 줄 로그 — 웹 프로그레스바 파서용.
실매 엔진과 무관. 형식:
OPTUNA_POST_PROGRESS pct=.. step=.. total=.. stage=.. axis=a/b 설명
"""
from __future__ import annotations
import threading
from typing import Any, Optional
_tls = threading.local()
def _ctx() -> dict:
c = getattr(_tls, "ctx", None)
if not isinstance(c, dict):
c = {
"log": None,
"step": 1,
"total": 1,
"stage": "idle",
"n_axes": 1,
"axis_i": 0,
"axis_trials": 0,
}
_tls.ctx = c
return c
def begin_job(log: Any, total: int) -> None:
c = _ctx()
c["log"] = log
c["total"] = max(1, int(total or 1))
c["step"] = 0
c["stage"] = "start"
c["n_axes"] = 1
c["axis_i"] = 0
c["axis_trials"] = 0
emit(stage="start", detail="후처리 시작")
def begin_unit(step: int, stage: str, detail: str = "") -> None:
c = _ctx()
c["step"] = max(1, int(step))
c["stage"] = str(stage or "")
c["n_axes"] = 1
c["axis_i"] = 0
c["axis_trials"] = 0
emit(stage=stage, detail=detail or stage)
def next_unit(stage: str, detail: str = "") -> int:
c = _ctx()
step = int(c.get("step") or 0) + 1
begin_unit(step, stage, detail)
return step
def set_ob_axes(n_axes: int, axis_trials: int) -> None:
c = _ctx()
c["n_axes"] = max(1, int(n_axes or 1))
c["axis_trials"] = max(0, int(axis_trials or 0))
def begin_ob_axis(axis_name: str, axis_i: int, axis_trials: Optional[int] = None) -> None:
c = _ctx()
c["axis_i"] = max(0, int(axis_i))
if axis_trials is not None:
c["axis_trials"] = max(0, int(axis_trials))
emit(
stage=str(c.get("stage") or "ob"),
detail=str(axis_name or ""),
axis_done=0,
axis_total=int(c.get("axis_trials") or 0),
)
def on_ob_axis_trial(done: int, total: int, axis_name: str) -> None:
emit(
stage=str(_ctx().get("stage") or "ob"),
detail=str(axis_name or ""),
axis_done=int(done),
axis_total=int(total),
)
def finish_job(log: Optional[Any] = None) -> None:
c = _ctx()
if log is not None:
c["log"] = log
c["step"] = int(c.get("total") or 1)
emit(stage="done", detail="", done=True)
def emit(
*,
stage: str,
detail: str = "",
axis_done: int = 0,
axis_total: int = 0,
done: bool = False,
) -> None:
c = _ctx()
log = c.get("log")
if log is None:
return
step = max(0, int(c.get("step") or 0))
total = max(1, int(c.get("total") or 1))
n_axes = max(1, int(c.get("n_axes") or 1))
axis_i = max(0, int(c.get("axis_i") or 0))
ax_tot = int(axis_total or c.get("axis_trials") or 0)
ax_done = int(axis_done or 0)
if done:
pct = 100.0
else:
inner = 0.0
if ax_tot > 0:
inner = (axis_i + min(1.0, ax_done / float(ax_tot))) / float(n_axes)
elif n_axes > 1:
inner = axis_i / float(n_axes)
frac = (max(0, step - 1) + min(1.0, inner)) / float(total)
pct = round(min(99.9, max(0.0, 100.0 * frac)), 1)
bits = [
f"OPTUNA_POST_PROGRESS pct={pct}",
f"step={max(1, step) if step else 0}",
f"total={total}",
f"stage={stage}",
f"axis={ax_done}/{ax_tot}",
]
if detail:
bits.append(str(detail).replace("\n", " ")[:80])
if done:
bits.append("상세가능")
try:
log.info("%s", " ".join(bits))
except Exception:
pass