refactor: enhance Optuna backtesting framework, optimize orderbook filtering, and update database management utilities.

This commit is contained in:
Your Name
2026-08-12 10:19:19 +09:00
parent cb7e5037a0
commit c6bd62a25f
218 changed files with 31613 additions and 759 deletions

View File

@@ -3,11 +3,12 @@
돌파매매 백테스트 공통 로더 — backtest_web / param_search 가
동일한 캔들·유니버스·손익 계산을 쓰도록 단일 진입점.
청산: ``check_sell_signal_breakout_live`` — EOD → 익절 → 어깨 → 손절 → 트레일.
청산: ``check_sell_signal_breakout_live`` — EOD → 익절 → 어깨 → 호가컷/손절호가(OFF) → 손절 → 트레일.
"""
from __future__ import annotations
from typing import Any, Dict, List, Optional, Tuple
import os
from kis_trader.backtest.backtest_portfolio_common import (
attach_scalp_trade_pnl,
@@ -209,6 +210,23 @@ def get_breakout_defaults_from_env_row(env: Dict[str, Any]) -> Dict[str, Any]:
"max_spread_pct": pick(
("BREAKOUT_ORDERBOOK_MAX_SPREAD_PCT",), 0.45, float,
),
# 수익구간 호가매도 / 손절호가 (UI: 이익·손실은 % 표시 · 기본 OFF)
"exit_ob_enabled": (
str(env.get("BREAKOUT_EXIT_OB_ENABLED") or "").strip().lower()
in ("1", "true", "t", "y", "yes", "on")
),
"exit_ob_ratio_min": pick(("BREAKOUT_EXIT_OB_RATIO_MIN",), 0.4, float),
"exit_ob_ma_window": pick(("BREAKOUT_EXIT_OB_MA_WINDOW",), 5, lambda v: int(float(v))),
"exit_ob_min_profit_pct": pct_ui(pick(("BREAKOUT_EXIT_OB_MIN_PROFIT_PCT",), 0.005, float)),
"exit_ob_min_hold_bars": pick(("BREAKOUT_EXIT_OB_MIN_HOLD_BARS",), 3, lambda v: int(float(v))),
"stop_ob_enabled": (
str(env.get("BREAKOUT_STOP_OB_ENABLED") or "").strip().lower()
in ("1", "true", "t", "y", "yes", "on")
),
"stop_ob_ratio_min": pick(("BREAKOUT_STOP_OB_RATIO_MIN",), 0.4, float),
"stop_ob_ma_window": pick(("BREAKOUT_STOP_OB_MA_WINDOW",), 5, lambda v: int(float(v))),
"stop_ob_min_loss_pct": pct_ui(pick(("BREAKOUT_STOP_OB_MIN_LOSS_PCT",), 0.003, float)),
"stop_ob_min_hold_bars": pick(("BREAKOUT_STOP_OB_MIN_HOLD_BARS",), 2, lambda v: int(float(v))),
}
@@ -362,6 +380,8 @@ def load_breakout_candles_by_code(
db, start_key, end_key, min_bars=min_bars,
)
candle_src = os.environ.get("CANDLE_SOURCE", "kis") or "kis"
ind_cols = ws_candles_select_indicator_cols(db)
codes_raw = db.conn.execute(
"SELECT DISTINCT code FROM ws_candles WHERE timeframe=1 "
@@ -375,10 +395,10 @@ def load_breakout_candles_by_code(
for code in codes:
rows = db.conn.execute(
f"SELECT candle_time, open, high, low, close, volume{ind_cols} "
"FROM ws_candles WHERE timeframe=1 AND code=%s "
"AND candle_time >= %s AND candle_time <= %s AND is_confirmed=1 "
"ORDER BY candle_time ASC",
f"SELECT candle_time, open, high, low, close, volume {ind_cols} "
f"FROM ws_candles WHERE timeframe=1 AND code=%s "
f"AND candle_time >= %s AND candle_time <= %s AND is_confirmed=1 "
f"ORDER BY candle_time ASC",
[code, start_key, end_key],
).fetchall()
if len(rows) < min_bars: