feat(backtest): 대대적인 Optuna 백테스트 웹 UI 및 백엔드 파이프라인 개편

- Web UI:
  - Optuna 탭 추가 및 mode_combo (최빈값 조합), 사후합격 Top 10 시각화 기능
  - 파라미터 분포(p25~p75, median, mode) 히스토그램 및 과적합(Overfit) 위험도 진단 UI 신설
  - 체크박스 렌더링 깨짐 현상을 네이티브(appearance: auto)로 강제 복구 (CSS)
  - 다단 트레일링 스탑, 꼬리 진입/돌파 손절 등 고급 조건 설정 폼 UI 고도화

- Backend (Optuna Jobs):
  - CLI 환경에서 구동된 Optuna json 결과물을 웹 대시보드로 읽어오는 import 기능 강화
  - JSON 메타데이터에 sort_by, mode, 호가 적용 여부 등 핵심 파라미터 파싱 누락 수정
  - optuna_mode_combo.py 등 최빈값 조합 및 후보군 2차 검증을 위한 신규 모듈 추가

- DB & Execution:
  - WebSocket 호가/틱 피드 수집 통계(api_feed_collect_stats) 메모리 캐시 최적화
  - KIS client 접속 키(approval_key) 등 인프라스트럭처 안정성 및 공유 관리 구조 개선
  - 테스트 및 디버깅용 briefing 마크다운 자동 생성 기능 추가
This commit is contained in:
Your Name
2026-09-01 02:47:51 +09:00
parent 3d0255519e
commit 9ba9ab73b6
92 changed files with 8211 additions and 487 deletions

View File

@@ -785,7 +785,7 @@ def append_learn_postprocess_anchors(
(data or {}).get("results")
or (data or {}).get("results_all")
or []
)[: max(1, int(top_n or 5))]
)[: max(1, int(top_n or 10))]
if not learn:
return
lg.info(
@@ -856,7 +856,7 @@ def append_stable_postprocess_anchors(
if any(str(a.get("role") or "") == "stable" for a in anchors):
return
from kis_trader.backtest.optuna_common import resolve_results_stable
stable, _meta = resolve_results_stable(data, top_n=max(1, int(top_n or 5)))
stable, _meta = resolve_results_stable(data, top_n=max(1, int(top_n or 10)))
# 후처리 중 JSON에 비어 있으면 재구성분 반영 (다음 요약·앵커 일치)
if not list((data or {}).get("results_stable") or []) and stable:
data["results_stable"] = list(stable)
@@ -976,7 +976,7 @@ def attach_topn_postprocess(
stable_preview: List[Any] = []
if _include_stable():
from kis_trader.backtest.optuna_common import resolve_results_stable
stable_preview, _sg = resolve_results_stable(data, top_n=max(1, int(top_n or 5)))
stable_preview, _sg = resolve_results_stable(data, top_n=max(1, int(top_n or 10)))
if not list((data or {}).get("results_stable") or []) and stable_preview:
data["results_stable"] = list(stable_preview)
if _sg:
@@ -985,7 +985,7 @@ def attach_topn_postprocess(
if not gated:
learn_preview = list(
(data or {}).get("results") or (data or {}).get("results_all") or []
)[: max(1, int(top_n or 5))]
)[: max(1, int(top_n or 10))]
n_units = len(gated) + len(learn_preview) + (len(stable_preview) if _include_stable() else 0)
if _include_mode():
n_units += 1