feat: Enhance Optuna integration and logging for backtesting framework

Changes:
- Added new API endpoints for continuing and confirming Optuna jobs, allowing for better management of ongoing studies.
- Introduced detailed logging for tick feed tracking and order book processing, improving traceability of vendor performance during backtests.
- Updated database schema to include new fields for managing Optuna study results, enhancing the ability to track study progress and outcomes.
- Refactored existing functions to utilize the new logging and tracking features, ensuring consistency across the backtesting framework.

Impact:
- These enhancements improve the robustness and transparency of the Optuna backtesting process, facilitating better analysis and optimization of trading strategies.
This commit is contained in:
Your Name
2026-08-21 19:05:23 +09:00
parent 0ecac7cb95
commit 0780b2cdd0
76 changed files with 4648 additions and 516 deletions

View File

@@ -577,6 +577,10 @@ ENV_CONFIG_KEYS = (
"WS_PRICE_MAX_AGE_SEC",
# 실매·옵투나 읽기 폴백 나이(초). 메인 체결시각 vs 지금. 넘기면 그 벤더 실패→2차→3차.
"LIVE_FEED_FALLBACK_MAX_AGE_SEC",
"TRIGGER_FEED_DETAIL_LOG",
# 옵투나/백테 피드 추적 (틱·호가 벤더 비율 + TRIGGER 샘플 상한)
"BT_FEED_DETAIL_LOG",
"BT_FEED_DETAIL_LOG_MAX",
"CANDLE_GARBAGE_FALLBACK",
# LS에 메인 틱/호가 유니버스(후보보유grace) 구독. 영구와 별개 owner.
"LS_FEED_FALLBACK_SUBSCRIBE",
@@ -587,6 +591,7 @@ ENV_CONFIG_KEYS = (
# 실매 매도: 마지막 WS 체결가 유지. 죽은선 감지 시에만 REST 1회(쿨다운).
"SELL_WS_STALE_REST_SEC",
"SELL_WS_STALE_REST_COOLDOWN_SEC",
"SELL_WS_LAST_RAM_MAX_AGE_SEC",
"WS_PROGRAM_COLLECT_ENABLED",
"WS_TRIGGER_EVAL_DB_BATCH_SIZE",
"WS_TRIGGER_EVAL_DB_FLUSH_SEC",
@@ -1900,6 +1905,37 @@ class TradeDB:
updated_at VARCHAR(30) NOT NULL
) CHARACTER SET utf8mb4
""")
# 8b-2. Optuna 스터디별 웹 결과·후처리 (kis_optuna 공식 테이블과 분리)
self.conn.execute_long("""
CREATE TABLE IF NOT EXISTS optuna_study_result (
study_name VARCHAR(255) NOT NULL COMMENT 'Optuna study 이름 (kis_optuna 과 동일)',
strategy VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'tail|momentum|us_momentum|breakout|scalp',
mode VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'tpe 등',
start_date VARCHAR(10) NULL COMMENT '백테 시작 YYYY-MM-DD',
end_date VARCHAR(10) NULL COMMENT '백테 종료 YYYY-MM-DD',
job_id VARCHAR(64) NULL COMMENT '웹 잡 id (참고)',
symbol VARCHAR(16) NULL COMMENT '해외 종목핀일 때만',
study_trials INT NOT NULL DEFAULT 0 COMMENT '스터디 총 완료 목표. 0이면 미설정',
n_complete INT NOT NULL DEFAULT 0 COMMENT 'kis_optuna 완료 trial 수 캐시',
n_running INT NOT NULL DEFAULT 0 COMMENT '진행 중 trial 수 캐시 (표시용)',
payload_json LONGTEXT NULL COMMENT 'results_all/gated/stable, mode_combo, postprocess_topn, overfit (웹 요약과 동일 키)',
briefing_md MEDIUMTEXT NULL COMMENT '브리핑 마크다운',
pp_needed TINYINT NOT NULL DEFAULT 0 COMMENT '1=목표 도달, 후처리 대기',
pp_status VARCHAR(16) NOT NULL DEFAULT 'idle' COMMENT 'idle|running|done|error',
pp_for_n INT NOT NULL DEFAULT 0 COMMENT '이 후처리가 반영한 n_complete',
pp_lock_until DATETIME NULL COMMENT '락 만료 (죽은 워커 복구)',
pp_lock_owner VARCHAR(64) NULL COMMENT '잠근 호스트/pid',
pp_error VARCHAR(500) NULL COMMENT '후처리 실패 메시지',
leftover_note VARCHAR(200) NULL COMMENT '웹 남은횟수 안내 캐시',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '행 갱신',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '최초 생성',
PRIMARY KEY (study_name),
KEY idx_pp_needed (pp_needed, pp_status),
KEY idx_job (job_id),
KEY idx_updated (updated_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Optuna 스터디별 웹 결과·후처리 산출 (파일 JSON 대체)'
""", timeout_sec=120)
# 8c. 인증/계좌/ID 전용 컬럼 저장소 (앱키/시크릿/ID 분리)
acols = ", ".join([f"`{k}` TEXT" for k in ENV_AUTH_KEYS])
self.conn.execute("""
@@ -1963,6 +1999,7 @@ class TradeDB:
session VARCHAR(8) DEFAULT NULL,
currency VARCHAR(8) NOT NULL DEFAULT 'KRW',
source VARCHAR(16) NOT NULL DEFAULT 'kis',
channel VARCHAR(10) NOT NULL DEFAULT 'ws',
recv_ts VARCHAR(30) NOT NULL,
KEY idx_ws_ticks_lookup (market, code, tick_time),
KEY idx_ws_ticks_recv (recv_ts)
@@ -1983,11 +2020,14 @@ class TradeDB:
session VARCHAR(8) DEFAULT NULL,
currency VARCHAR(8) NOT NULL DEFAULT 'USD',
source VARCHAR(16) NOT NULL DEFAULT 'kis_us',
channel VARCHAR(10) NOT NULL DEFAULT 'ws',
recv_ts VARCHAR(30) NOT NULL,
KEY idx_ws_ticks_us_lookup (market, code, tick_time),
KEY idx_ws_ticks_us_recv (recv_ts)
) ENGINE=InnoDB CHARACTER SET utf8mb4
""")
self._migrate_ws_ticks_extra_columns("ws_ticks")
self._migrate_ws_ticks_extra_columns("ws_ticks_us")
# 11b. TRIGGER 호가 스냅샷 (키움 0D — 백테·파람서치 재현)
self.conn.execute("""
@@ -4830,6 +4870,7 @@ class TradeDB:
session VARCHAR(8) DEFAULT NULL,
currency VARCHAR(8) NOT NULL DEFAULT 'KRW',
source VARCHAR(16) NOT NULL DEFAULT 'kis',
channel VARCHAR(10) NOT NULL DEFAULT 'ws',
recv_ts VARCHAR(30) NOT NULL,
cntr_str DOUBLE DEFAULT NULL,
upper_limit_time VARCHAR(8) DEFAULT NULL,
@@ -4858,6 +4899,8 @@ class TradeDB:
("cntr_str", "DOUBLE DEFAULT NULL"),
("upper_limit_time", "VARCHAR(8) DEFAULT NULL"),
("tick_time_raw", "VARCHAR(64) DEFAULT NULL"),
# 봉 ws_candles 와 동일: source=증권사, channel=경로(ws|rest). 틱은 WS만 적재 → 기본 ws.
("channel", "VARCHAR(10) NOT NULL DEFAULT 'ws' COMMENT 'ws|rest' AFTER `source`"),
):
if col not in existing:
try:
@@ -4896,6 +4939,7 @@ class TradeDB:
session VARCHAR(8) DEFAULT NULL,
currency VARCHAR(8) NOT NULL DEFAULT 'USD',
source VARCHAR(16) NOT NULL DEFAULT 'kis_us',
channel VARCHAR(10) NOT NULL DEFAULT 'ws',
recv_ts VARCHAR(30) NOT NULL,
cntr_str DOUBLE DEFAULT NULL,
upper_limit_time VARCHAR(8) DEFAULT NULL,
@@ -4927,6 +4971,9 @@ class TradeDB:
return 0
rows = []
for it in batch:
ch = str(it.get("channel") or "ws").strip().lower() or "ws"
if ch not in ("ws", "rest"):
ch = "ws"
rows.append((
(it.get("market") or default_market)[:8],
(it.get("exchange") or None),
@@ -4938,6 +4985,7 @@ class TradeDB:
(it.get("session") or None),
(it.get("currency") or default_currency)[:8],
(it.get("source") or default_source)[:16],
ch[:10],
it.get("recv_ts") or datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
float(it["cntr_str"]) if it.get("cntr_str") is not None else None,
(it.get("upper_limit_time") or None),
@@ -4945,7 +4993,7 @@ class TradeDB:
))
exec_conn = conn or self.conn
try:
ph = "(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
ph = "(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
placeholders = ", ".join([ph] * len(rows))
flat: List[Any] = []
for row in rows:
@@ -4954,7 +5002,7 @@ class TradeDB:
f"""
INSERT INTO {table}
(market, exchange, code, tick_time, price, volume,
tick_seq, session, currency, source, recv_ts,
tick_seq, session, currency, source, channel, recv_ts,
cntr_str, upper_limit_time, tick_time_raw)
VALUES {placeholders}
""",