- 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 마크다운 자동 생성 기능 추가
196 lines
7.1 KiB
Bash
Executable File
196 lines
7.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# 4전략 Optuna mode=tpe 순차 실행 (병렬 X — RAM/틱 로딩 OOM 방지)
|
|
# 기본 기간: 어제~오늘(호출 시 START/END 오버라이드)
|
|
# --apply-best 없음 — 결과 JSON만. apply 는 사용자 승인 후.
|
|
#
|
|
# 사용:
|
|
# nohup bash scripts/run_optuna_4strat_tpe_seq.sh >> logs/optuna_4strat_tpe_master.log 2>&1 &
|
|
# tail -f logs/optuna_4strat_tpe_latest_master.logpath # 경로만
|
|
# tail -f "$(cat logs/optuna_4strat_tpe_latest_master.logpath)"
|
|
#
|
|
# START=2026-07-20 END=2026-07-21 TRIALS=200 bash scripts/run_optuna_4strat_tpe_seq.sh
|
|
|
|
set -euo pipefail
|
|
cd /home/hoon/kis_bot
|
|
mkdir -p logs kis_trader/backtest/results
|
|
|
|
START="${START:-2026-07-20}"
|
|
END="${END:-2026-07-21}"
|
|
MODE="${MODE:-tpe}"
|
|
TRIALS="${TRIALS:-200}"
|
|
STUDY_TRIALS="${STUDY_TRIALS:-}"
|
|
MIN_TRADES="${MIN_TRADES:-1}"
|
|
# 탐색 게이트 OFF — TPE 학습. 사후 후보는 JSON results_gated (REPORT_MIN_*)
|
|
MIN_WIN_RATE="${MIN_WIN_RATE:-0}"
|
|
MIN_PF="${MIN_PF:-0}"
|
|
# 공백 구분 — 기본 국내4 (해외 us_momentum 은 STRATEGIES 에 넣을 때만)
|
|
# STRATEGIES="momentum tail" bash ...
|
|
# STRATEGIES="us_momentum momentum" bash ...
|
|
STRATEGIES="${STRATEGIES:-momentum tail breakout scalp}"
|
|
# 꼬리 TPE 진입모드 — 한 스터디에 섞지 않음. 공백 구분 시 순차 2회.
|
|
# TAIL_OPTUNA_ENTRY_MODES="align limit_atr"
|
|
TAIL_OPTUNA_ENTRY_MODES="${TAIL_OPTUNA_ENTRY_MODES:-align}"
|
|
# 돌파 TPE 손절모드 — 한 스터디에 섞지 않음. 공백 구분 시 순차.
|
|
# BREAKOUT_OPTUNA_SL_MODES="fixed atr"
|
|
BREAKOUT_OPTUNA_SL_MODES="${BREAKOUT_OPTUNA_SL_MODES:-fixed}"
|
|
# 돌파 TPE 호가 스위치 — trial 축 아님. 손절×호가 최대 4순차.
|
|
# BREAKOUT_OPTUNA_OB_MODES="off on"
|
|
BREAKOUT_OPTUNA_OB_MODES="${BREAKOUT_OPTUNA_OB_MODES:-off}"
|
|
# kiwoom|ls — 웹 Optuna 이력소스 / CLI UNIVERSE_HISTORY_SOURCE
|
|
UNIVERSE_HISTORY_SOURCE="${UNIVERSE_HISTORY_SOURCE:-${BACKTEST_UNIVERSE_HISTORY_SOURCE:-kiwoom}}"
|
|
# 웹 순차 잡 ID (전략/조합별 1·2차 state 파일 prefix)
|
|
OPTUNA_SEQ_JOB_ID="${OPTUNA_SEQ_JOB_ID:-seq}"
|
|
SORT_BY="${SORT_BY:-score}"
|
|
PY="${PY:-.venv/bin/python}"
|
|
TS0="$(date +%Y%m%d_%H%M%S)"
|
|
MASTER="logs/optuna_4strat_tpe_${START}_${END}_${TS0}_master.log"
|
|
|
|
{
|
|
echo "======== Optuna 4전략 TPE 순차 시작 $(date -Is) ========"
|
|
echo "START=$START END=$END MODE=$MODE TRIALS=$TRIALS STUDY_TRIALS=${STUDY_TRIALS:-}"
|
|
echo "STRATEGIES=$STRATEGIES"
|
|
echo "TAIL_OPTUNA_ENTRY_MODES=$TAIL_OPTUNA_ENTRY_MODES"
|
|
echo "BREAKOUT_OPTUNA_SL_MODES=$BREAKOUT_OPTUNA_SL_MODES"
|
|
echo "BREAKOUT_OPTUNA_OB_MODES=$BREAKOUT_OPTUNA_OB_MODES"
|
|
echo "UNIVERSE_HISTORY_SOURCE=$UNIVERSE_HISTORY_SOURCE"
|
|
echo "min_wr=$MIN_WIN_RATE min_pf=$MIN_PF min_trades=$MIN_TRADES"
|
|
echo "apply-best=OFF · 전략/조합마다 1·2차 TPE 연쇄 · n_jobs=1 (사후 results_gated + briefing.md)"
|
|
echo "master_log=$MASTER"
|
|
free -h | sed -n '1,2p'
|
|
df -h / | tail -1
|
|
} | tee -a "$MASTER"
|
|
echo "$MASTER" > logs/optuna_4strat_tpe_latest_master.logpath
|
|
|
|
run_one() {
|
|
local strat="$1"
|
|
local entry_mode="${2:-}"
|
|
local sl_mode="${3:-}"
|
|
local ob_mode="${4:-off}"
|
|
local ts step_job_id refine_log state_file sort_by bo_extra extra min_trades_arg
|
|
ts="$(date +%Y%m%d_%H%M%S)"
|
|
sort_by="${SORT_BY:-score}"
|
|
case "$sort_by" in
|
|
score|score_legacy|pnl|daily_avg|win_rate) ;;
|
|
*) sort_by="score" ;;
|
|
esac
|
|
|
|
if [[ "$strat" == "breakout" && -n "$sl_mode" ]]; then
|
|
bo_extra="${sl_mode}_ob_${ob_mode}"
|
|
extra="$bo_extra"
|
|
elif [[ "$strat" == "tail" && -n "$entry_mode" ]]; then
|
|
extra="$entry_mode"
|
|
else
|
|
extra=""
|
|
fi
|
|
|
|
step_job_id="${OPTUNA_SEQ_JOB_ID}_${strat}${extra:+_${extra}}_${ts}"
|
|
refine_log="logs/optuna_seq_refine_${step_job_id}.log"
|
|
state_file="logs/${step_job_id}_refine_state.json"
|
|
|
|
min_trades_arg=$("$PY" -c "from kis_trader.backtest.optuna_common import resolve_optuna_min_trades; print(resolve_optuna_min_trades('${START}', '${END}', '${strat}')['min_trades'])")
|
|
|
|
{
|
|
echo ""
|
|
echo "-------- [$strat${entry_mode:+/$entry_mode}${sl_mode:+/$sl_mode}${bo_extra:+/$bo_extra}] START $(date -Is) 1·2차 step=$step_job_id --------"
|
|
} | tee -a "$MASTER"
|
|
|
|
if [[ -n "${OPTUNA_SEQ_ACTIVE_FILE:-}" ]]; then
|
|
{
|
|
echo "strategy=$strat"
|
|
echo "entry_mode=${entry_mode:-}"
|
|
echo "sl_mode=${sl_mode:-}"
|
|
echo "ob_mode=${ob_mode:-}"
|
|
echo "extra=${extra}"
|
|
echo "refine_state_path=$state_file"
|
|
} > "$OPTUNA_SEQ_ACTIVE_FILE"
|
|
fi
|
|
|
|
set +e
|
|
local refine_args=(
|
|
--job-id "$step_job_id"
|
|
--strategy "$strat"
|
|
--mode "$MODE"
|
|
--start "$START"
|
|
--end "$END"
|
|
--trials "$TRIALS"
|
|
--sort-by "$sort_by"
|
|
--min-trades "$min_trades_arg"
|
|
--universe-history-source "$UNIVERSE_HISTORY_SOURCE"
|
|
)
|
|
if [[ -n "${STUDY_TRIALS:-}" && "${STUDY_TRIALS}" != "0" ]]; then
|
|
refine_args+=(--study-trials "$STUDY_TRIALS")
|
|
fi
|
|
if [[ "$strat" == "tail" && -n "$entry_mode" ]]; then
|
|
refine_args+=(--entry-mode "$entry_mode")
|
|
fi
|
|
if [[ "$strat" == "breakout" && -n "$sl_mode" ]]; then
|
|
refine_args+=(--sl-mode "$sl_mode" --ob-mode "$ob_mode")
|
|
fi
|
|
if [[ -n "${CANDLE_SOURCE:-}" ]]; then
|
|
refine_args+=(--candle-source "$CANDLE_SOURCE")
|
|
fi
|
|
if [[ -n "${TICK_SOURCE:-}" ]]; then
|
|
refine_args+=(--tick-source "$TICK_SOURCE")
|
|
fi
|
|
if [[ -n "${OB_SOURCE:-}" ]]; then
|
|
refine_args+=(--ob-source "$OB_SOURCE")
|
|
fi
|
|
|
|
"$PY" -u kis_trader/backtest/optuna_mode_refine_runner.py "${refine_args[@]}" >"$refine_log" 2>&1
|
|
local rc=$?
|
|
set -e
|
|
|
|
local result_json briefing_md
|
|
result_json=""
|
|
briefing_md=""
|
|
if [[ -f "$state_file" ]]; then
|
|
result_json=$("$PY" -c "import json; print(json.load(open('${state_file}')).get('result_json') or '')" 2>/dev/null || true)
|
|
if [[ -n "$result_json" && -f "$result_json" ]]; then
|
|
echo "OPTUNA_RESULT_JSON=$result_json" | tee -a "$MASTER"
|
|
briefing_md="${result_json%.json}.briefing.md"
|
|
if [[ -f "$briefing_md" ]]; then
|
|
echo "OPTUNA_BRIEFING_MD=$briefing_md" | tee -a "$MASTER"
|
|
fi
|
|
fi
|
|
fi
|
|
if [[ -z "$result_json" ]]; then
|
|
grep -E 'OPTUNA_RESULT_JSON=' "$refine_log" | tail -n 1 | tee -a "$MASTER" || true
|
|
fi
|
|
|
|
{
|
|
echo "-------- [$strat${entry_mode:+/$entry_mode}${sl_mode:+/$sl_mode}${bo_extra:+/$bo_extra}] END rc=$rc $(date -Is) --------"
|
|
echo "REFINE_LOG=$refine_log"
|
|
echo "STATE=$state_file"
|
|
grep -E 'OPTUNA_RESULT_JSON=|OPTUNA_BRIEFING_MD=|✅ 1·2차|❌ 1·2차|phase1|phase2|Traceback' "$refine_log" | tail -n 24 || true
|
|
} | tee -a "$MASTER"
|
|
|
|
if [[ "$rc" -ne 0 ]]; then
|
|
echo "⚠️ [$strat] 1·2차 실패 rc=$rc — 다음 전략 계속" | tee -a "$MASTER"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
for s in $STRATEGIES; do
|
|
if [[ "$s" == "tail" ]]; then
|
|
for em in $TAIL_OPTUNA_ENTRY_MODES; do
|
|
run_one tail "$em"
|
|
done
|
|
elif [[ "$s" == "breakout" ]]; then
|
|
for sm in $BREAKOUT_OPTUNA_SL_MODES; do
|
|
for om in $BREAKOUT_OPTUNA_OB_MODES; do
|
|
run_one breakout "" "$sm" "$om"
|
|
done
|
|
done
|
|
else
|
|
run_one "$s"
|
|
fi
|
|
done
|
|
|
|
{
|
|
echo ""
|
|
echo "======== ALL DONE $(date -Is) ========"
|
|
echo "apply 하지 않음. JSON= kis_trader/backtest/results/optuna_*_tpe_*.json"
|
|
echo "브리핑= 같은 이름 .briefing.md (이전 장 / 앞으로 장)"
|
|
echo "검증: .venv/bin/python scripts/verify_optuna_tpe_apply_dryrun.py"
|
|
} | tee -a "$MASTER"
|