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:
52
database.py
52
database.py
@@ -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}
|
||||
""",
|
||||
|
||||
Reference in New Issue
Block a user