feat(옵투나·웹): 후처리 재탐색·ob_modes·적용감사·수집통계

- Optuna web jobs/TPE/apply snapshot·틱로더 정합, jobs limit·감사로그
- 백테 UI 호가모드·후보 적용 흐름, feed_collect_stats API/탭
- 가설검증·교차검증 룰, 4전략 스모크·OB slot41 진단 스크립트

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Your Name
2026-08-27 15:23:44 +09:00
parent 5e44b86f8b
commit 8fbba264ba
30 changed files with 2973 additions and 186 deletions

View File

@@ -70,6 +70,7 @@
<li class="nav-item"><a class="nav-link active" data-tab="actual" href="#">📊 실거래 분석</a></li>
<li class="nav-item"><a class="nav-link" data-tab="dashboard" href="#">📈 오늘 운영</a></li>
<li class="nav-item"><a class="nav-link" data-tab="liveconfig" href="#">⚙️ 운영 설정</a></li>
<li class="nav-item"><a class="nav-link" data-tab="feedstats" href="#">📡 수집통계</a></li>
<li class="nav-item"><a class="nav-link" data-tab="portfolio" href="#">💼 보유·매도</a></li>
<li class="nav-item"><a class="nav-link" data-tab="backtest" href="#">📉 스캘핑 백테스트</a></li>
<li class="nav-item"><a class="nav-link" data-tab="tail" href="#">🎣 꼬리잡기 백테스트</a></li>
@@ -345,6 +346,153 @@
</div>
</div>
<!-- ═══ 수집통계 (일자별 틱·호가 벤더) ═══════════════════════════════════ -->
<div id="tab-feedstats" style="display:none">
<div class="card p-3 mb-3">
<div class="row g-3 align-items-end">
<div class="col-auto">
<label class="form-label param-row" style="color:var(--muted);font-size:12px">기준일 (KST)</label>
<input type="date" id="fs_date" class="form-control form-control-sm" style="width:160px">
</div>
<div class="col-auto">
<button class="btn btn-sm btn-primary" onclick="fsLoad(false)">🔄 조회</button>
</div>
<div class="col-auto">
<button class="btn btn-sm btn-outline-secondary" onclick="fsLoad(true)" title="나이컷·호가0종목 교집합 (느림)">⏱ 상세 집계</button>
</div>
<div class="col">
<small id="fs_meta" class="text-muted"></small>
</div>
</div>
<div style="font-size:12px;color:var(--muted);line-height:1.55;margin-top:8px">
<b>메인 표</b> = 증권사별 틱 vs 호가 대비 (MODE=tick → 호가행 ≤ 틱행이 정상).
LS 틱=`ls_ws_ticks` · LS 호가=`ls_ws_orderbook`. filter_eval 은 진입스냅(별도).
최근 N일 표는 제거(전수 GROUP BY 폭주). 나이컷·호가0종목은 「상세 집계」.
</div>
</div>
<div id="fs_env" class="mb-2" style="font-size:12px"></div>
<div id="fs_notes" class="mb-3" style="font-size:13px"></div>
<div class="card p-3 mb-3">
<div class="fw-semibold mb-2" style="font-size:14px">당일 · 증권사별 틱 / 호가 대비</div>
<div class="table-responsive">
<table class="table table-sm table-bordered mb-0" style="font-size:13px">
<thead class="table-light">
<tr>
<th>증권사</th>
<th class="text-end">틱행</th>
<th class="text-end">틱종목</th>
<th class="text-end">호가행</th>
<th class="text-end">호가종목</th>
<th class="text-end" title="호가행÷틱행">호가/틱</th>
<th class="text-end">호가/틱%</th>
<th>비고</th>
</tr>
</thead>
<tbody id="fs_vendor_tbody"></tbody>
</table>
</div>
<div id="fs_vendor_foot" class="mt-2" style="font-size:12px;color:var(--muted)"></div>
</div>
<div class="card p-3 mb-3">
<div class="fw-semibold mb-2" style="font-size:14px">폴백나이(3초컷) 합격 · 옵투나 usable</div>
<div style="font-size:12px;color:var(--muted);margin-bottom:8px" id="fs_age_note"></div>
<div class="table-responsive">
<table class="table table-sm table-bordered mb-0" style="font-size:12px">
<thead class="table-light">
<tr>
<th>채널</th>
<th>source</th>
<th class="text-end">총행</th>
<th class="text-end" title="lag≤나이">합격</th>
<th class="text-end" title="lag>나이">탈락</th>
<th class="text-end" title="lag 미상=유지">미상</th>
<th class="text-end" title="합격+미상">usable</th>
<th class="text-end">usable%</th>
<th class="text-end">탈락%</th>
<th class="text-end" title="그 분 틱이 전부 탈락">전량탈락분</th>
</tr>
</thead>
<tbody id="fs_age_tbody"></tbody>
</table>
</div>
</div>
<div class="card p-3 mb-3">
<div class="fw-semibold mb-2" style="font-size:14px">틱↔호가 공백 · 동기율 (종목 단위)</div>
<div class="table-responsive">
<table class="table table-sm table-hover mb-0" style="font-size:12px">
<thead>
<tr>
<th>벤더</th>
<th class="text-end">틱행</th>
<th class="text-end">틱종목</th>
<th class="text-end">호가행</th>
<th class="text-end">호가종목</th>
<th class="text-end" title="틱은 있는데 호가행이 한 줄도 없는 종목">호가0종목</th>
<th class="text-end">호가0%</th>
<th class="text-end">호가/틱행%</th>
<th class="text-end">공백행%</th>
</tr>
</thead>
<tbody id="fs_cov_tbody"></tbody>
</table>
</div>
<div id="fs_overlap" class="mt-2" style="font-size:12px;color:var(--muted)"></div>
</div>
<details class="mb-3">
<summary style="cursor:pointer;font-size:13px;color:var(--muted)">세부 source / filter_eval / LS 테이블 (접기)</summary>
<div class="row g-3 mt-1">
<div class="col-md-6">
<div class="card p-3">
<div class="fw-semibold mb-2" style="font-size:13px">틱 by source (<code>ws_ticks</code>)</div>
<div class="table-responsive">
<table class="table table-sm mb-0" style="font-size:12px">
<thead><tr><th>source</th><th class="text-end"></th><th class="text-end">종목</th></tr></thead>
<tbody id="fs_ticks_tbody"></tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card p-3">
<div class="fw-semibold mb-2" style="font-size:13px">호가 by source (<code>ws_orderbook</code>)</div>
<div class="table-responsive">
<table class="table table-sm mb-0" style="font-size:12px">
<thead><tr><th>source</th><th class="text-end"></th><th class="text-end">종목</th></tr></thead>
<tbody id="fs_ob_tbody"></tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6 mt-2">
<div class="card p-3">
<div class="fw-semibold mb-2" style="font-size:13px">filter_eval by 전략</div>
<div class="table-responsive">
<table class="table table-sm mb-0" style="font-size:12px">
<thead><tr><th>strategy</th><th class="text-end"></th><th class="text-end">종목</th></tr></thead>
<tbody id="fs_fe_tbody"></tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6 mt-2">
<div class="card p-3">
<div class="fw-semibold mb-2" style="font-size:13px">LS·전용 테이블</div>
<div class="table-responsive">
<table class="table table-sm mb-0" style="font-size:12px">
<thead><tr><th>테이블</th><th class="text-end"></th><th class="text-end">종목</th></tr></thead>
<tbody id="fs_ls_tbody"></tbody>
</table>
</div>
</div>
</div>
</div>
</details>
</div>
<!-- ═══ 운영 설정 (실매 env 실시간 조회·저장) ═════════════════════════════ -->
<div id="tab-liveconfig" style="display:none">
<div class="card p-3 mb-3">
@@ -575,15 +723,15 @@
</div>
<div class="col-4 col-md-1">
<label class="form-label param-row">손절(%)</label>
<input type="number" class="form-control" id="bt_sl" value="1.5" min="0.3" max="5" step="0.1">
<input type="number" class="form-control" id="bt_sl" value="1.5" min="0.3" max="5" step="0.1" readonly tabindex="-1" title="DB·Optuna 적용값 · 수정은 Optuna「적용」" style="background:#0d1117;color:#f0a060">
</div>
<div class="col-4 col-md-1">
<label class="form-label param-row">익절(%)</label>
<input type="number" class="form-control" id="bt_tp" value="1.5" min="0.3" max="5" step="0.1">
<input type="number" class="form-control" id="bt_tp" value="1.5" min="0.3" max="5" step="0.1" readonly tabindex="-1" title="DB·Optuna 적용값 · 수정은 Optuna「적용」" style="background:#0d1117;color:#f0a060">
</div>
<div class="col-4 col-md-1">
<label class="form-label param-row" title="익절 % 상한 — tp_pct가 이보다 크면 목표가는 이 값으로 캡">익절상한%</label>
<input type="number" class="form-control" id="bt_tp_max" value="2.0" min="0.3" max="10" step="0.1">
<input type="number" class="form-control" id="bt_tp_max" value="2.0" min="0.3" max="10" step="0.1" readonly tabindex="-1" title="DB·Optuna 적용값 · 수정은 Optuna「적용」" style="background:#0d1117;color:#f0a060">
</div>
<div class="col-4 col-md-1">
<label class="form-label param-row">낙폭필터(%)</label>
@@ -666,14 +814,26 @@
</div>
</div>
<!-- 호가·휩쏘 (DB 읽기전용) — Optuna 적용값 확인. 입력 불가 -->
<!-- 호가·익절·손절·휩쏘 (DB 읽기전용) — Optuna 적용값 확인. 입력 불가 -->
<div class="row g-2 align-items-end mt-2 p-2 rounded" style="background:#161b22;border:1px solid #3d2a1a" id="bt_ob_whip_ro_box">
<div class="col-12">
<span class="text-muted" style="font-size:11px">
<b style="color:#f0a060">호가·휩쏘 (DB · 읽기전용)</b>
— Optuna「적용」후 여기 숫자로 확인. 수정 불가 · 백테는 DB값 사용
<b style="color:#f0a060">호가·익절·손절·휩쏘 (DB · 읽기전용)</b>
— Optuna「적용」후 여기로 확인. 수정 불가 · 백테는 DB값 사용
</span>
</div>
<div class="col-4 col-md-1">
<label class="form-label param-row" title="SCALP_STOP_LOSS_PCT">손절%<span class="d-block text-muted fw-normal" style="font-size:10px">SL</span></label>
<input type="number" class="form-control form-control-sm" id="bt_sl_ro" readonly tabindex="-1" style="background:#0d1117;color:#f0a060">
</div>
<div class="col-4 col-md-1">
<label class="form-label param-row" title="SCALP_TAKE_PROFIT_PCT">익절%<span class="d-block text-muted fw-normal" style="font-size:10px">TP</span></label>
<input type="number" class="form-control form-control-sm" id="bt_tp_ro" readonly tabindex="-1" style="background:#0d1117;color:#f0a060">
</div>
<div class="col-4 col-md-1">
<label class="form-label param-row" title="SCALP_TP_MAX_PCT">익절상한%<span class="d-block text-muted fw-normal" style="font-size:10px">TP_MAX</span></label>
<input type="number" class="form-control form-control-sm" id="bt_tp_max_ro" readonly tabindex="-1" style="background:#0d1117;color:#f0a060">
</div>
<div class="col-auto">
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="bt_ob_ro_on" disabled>
@@ -1202,11 +1362,11 @@
</div>
<div class="col-6 col-md-2" title="이 백테 1회에만 적용 (실매 미반영). 저장 시에만 DB 반영">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="tl_ob_filter" checked>
<input class="form-check-input" type="checkbox" id="tl_ob_filter">
<label class="form-check-label" for="tl_ob_filter" style="font-size:12px">호가필터(백테)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="tl_pg_filter" checked>
<input class="form-check-input" type="checkbox" id="tl_pg_filter">
<label class="form-check-label" for="tl_pg_filter" style="font-size:12px">프로그램필터(백테)</label>
</div>
</div>
@@ -1780,11 +1940,11 @@
</div>
<div class="col-6 col-md-2" title="이 백테 1회에만 적용 (실매 미반영). 저장 시에만 DB 반영">
<div class="form-check mt-1">
<input class="form-check-input" type="checkbox" id="bo_ob_filter" checked>
<input class="form-check-input" type="checkbox" id="bo_ob_filter">
<label class="form-check-label" for="bo_ob_filter" style="font-size:12px">호가필터(백테)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="bo_pg_filter" checked>
<input class="form-check-input" type="checkbox" id="bo_pg_filter">
<label class="form-check-label" for="bo_pg_filter" style="font-size:12px">프로그램필터(백테)</label>
</div>
</div>
@@ -2474,13 +2634,13 @@
<div class="col-12 col-md-2">
<label class="form-label param-row" title="이 백테 1회에만 적용 (실매 미반영). 저장 시에만 DB 반영">TRIGGER 필터(백테)<span class="d-block text-muted fw-normal" style="font-size:10px">호가·프로그램 · 끄고/켜고 비교용</span></label>
<div class="form-check mt-1">
<input class="form-check-input" type="checkbox" id="mom_ob_filter" checked disabled>
<label class="form-check-label" for="mom_ob_filter">호가필터 ON (DB·읽기전용)</label>
<input class="form-check-input" type="checkbox" id="mom_ob_filter" disabled>
<label class="form-check-label" for="mom_ob_filter" id="mom_ob_filter_lbl">호가필터 (DB·읽기전용)</label>
</div>
<span id="mom_ob_filter_note" class="d-block text-muted" style="font-size:10px"></span>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="mom_pg_filter" checked>
<label class="form-check-label" for="mom_pg_filter">프로그램필터 ON</label>
<input class="form-check-input" type="checkbox" id="mom_pg_filter">
<label class="form-check-label" for="mom_pg_filter" id="mom_pg_filter_lbl">프로그램필터</label>
</div>
<span id="mom_pg_filter_note" class="d-block text-muted" style="font-size:10px"></span>
</div>
@@ -3841,6 +4001,17 @@
<small class="text-muted">ATR배수</small>
</label>
</span>
<span class="d-inline-flex flex-wrap align-items-center gap-1 px-2 py-1" style="border:1px solid var(--border);border-radius:6px" title="돌파 호가=스터디 스위치(trial 축 아님). off/on 각각 별도 스터디. 손절×호가 최대 4순차.">
<span class="text-muted" style="font-size:12px">돌파 호가</span>
<label class="form-check form-check-inline mb-0">
<input class="form-check-input opt-bo-ob" type="checkbox" id="opt_bo_ob_off" value="off" checked> off
<small class="text-muted">필터OFF</small>
</label>
<label class="form-check form-check-inline mb-0">
<input class="form-check-input opt-bo-ob" type="checkbox" id="opt_bo_ob_on" value="on"> on
<small class="text-muted">필터ON</small>
</label>
</span>
<label class="form-check form-check-inline mb-0"><input class="form-check-input opt-strat-cb" type="checkbox" value="scalp"> 스캘핑</label>
<button type="button" class="btn btn-sm btn-outline-secondary py-0" onclick="optunaPickKr4()">국내4</button>
<button type="button" class="btn btn-sm btn-outline-secondary py-0" onclick="optunaPickNone()">해제</button>
@@ -3999,6 +4170,8 @@
<div id="opt_compare_tbody" class="d-none"></div>
</div>
<div id="opt_tpe_ob_banner" class="small mb-2 text-muted"></div>
<div class="card p-3 mb-3">
<h6>학습 Top10 <small class="text-muted">results · Optuna 목적함수 1위 풀 · 진행바 best=여기 1위 · OPTUNA_POST_TOP_N</small></h6>
<div class="table-responsive">
@@ -4007,6 +4180,7 @@
<tr>
<th>#</th><th>trial</th><th class="text-end">거래</th>
<th class="text-end">승률</th><th class="text-end">PF</th><th class="text-end">PnL</th>
<th title="본 TPE: 호가ON/OFF·spr/r/ask · 익절%/상한 · 손절% · 휩쏘ON/OFF·수치 (사후8방 아님)">호가·익절·손절·휩쏘</th>
<th class="text-end">손실일</th><th class="text-end">최악일</th>
<th class="text-end" title="높을수록 좋음. 만점 없음. 일평균PnL λ×일표준편차(원).">안정점수 ↑</th>
<th class="text-end" title="높을수록 위험. 0~100. 이 숫자만 믿으면 내일 틀릴 수 있는 정도(추정).">과적합% ↓</th>
@@ -4014,20 +4188,21 @@
</tr>
</thead>
<tbody id="opt_top5_learn_tbody">
<tr><td colspan="11" class="text-muted">완료 후 표시</td></tr>
<tr><td colspan="12" class="text-muted">완료 후 표시</td></tr>
</tbody>
</table>
</div>
</div>
<div class="card p-3 mb-3">
<h6>사후합격 Top10 <small class="text-muted">results_gated · 「상세」=아래 후처리 표(8방 호가)</small></h6>
<h6>사후합격 Top10 <small class="text-muted">results_gated · 「호가·익절·손절·휩쏘」열=본TPE 수치 · 사후8방은 TPE호가포함 시 비는 게 정상</small></h6>
<div class="table-responsive">
<table class="table table-sm table-hover mb-0" style="font-size:12px">
<thead>
<tr>
<th>#</th><th>trial</th><th class="text-end">거래</th>
<th class="text-end">승률</th><th class="text-end">PF</th><th class="text-end">PnL</th>
<th title="본 TPE: 호가ON/OFF·spr/r/ask · 익절%/상한 · 손절% · 휩쏘ON/OFF·수치 (사후8방 아님)">호가·익절·손절·휩쏘</th>
<th class="text-end">손실일</th><th class="text-end">최악일</th>
<th class="text-end" title="높을수록 좋음. 만점 없음. 일평균PnL λ×일표준편차(원).">안정점수 ↑</th>
<th class="text-end" title="높을수록 위험. 0~100. 이 숫자만 믿으면 내일 틀릴 수 있는 정도(추정).">과적합% ↓</th>
@@ -4035,7 +4210,7 @@
</tr>
</thead>
<tbody id="opt_top5_tbody">
<tr><td colspan="11" class="text-muted">완료 후 표시</td></tr>
<tr><td colspan="12" class="text-muted">완료 후 표시</td></tr>
</tbody>
</table>
</div>
@@ -4050,6 +4225,7 @@
<tr>
<th>#</th><th>trial</th><th class="text-end">거래</th>
<th class="text-end">승률</th><th class="text-end">PF</th><th class="text-end">PnL</th>
<th title="본 TPE: 호가ON/OFF·spr/r/ask · 익절%/상한 · 손절% · 휩쏘ON/OFF·수치 (사후8방 아님)">호가·익절·손절·휩쏘</th>
<th class="text-end">손실일</th><th class="text-end">최악일</th>
<th class="text-end" title="높을수록 좋음. 만점 없음. 일평균PnL λ×일표준편차(원).">안정점수 ↑</th>
<th class="text-end" title="높을수록 위험. 0~100. 이 숫자만 믿으면 내일 틀릴 수 있는 정도(추정).">과적합% ↓</th>
@@ -4057,7 +4233,7 @@
</tr>
</thead>
<tbody id="opt_top5_mode_tbody">
<tr><td colspan="11" class="text-muted">완료 후 표시 (mode 실측 1행 · 사후합격과 같은 열)</td></tr>
<tr><td colspan="12" class="text-muted">완료 후 표시 (mode 실측 1행 · 사후합격과 같은 열)</td></tr>
</tbody>
</table>
</div>
@@ -4072,6 +4248,7 @@
<tr>
<th>#</th><th>trial</th><th class="text-end">거래</th>
<th class="text-end">승률</th><th class="text-end">PF</th><th class="text-end">PnL</th>
<th title="본 TPE: 호가ON/OFF·spr/r/ask · 익절%/상한 · 손절% · 휩쏘ON/OFF·수치 (사후8방 아님)">호가·익절·손절·휩쏘</th>
<th class="text-end">손실일</th><th class="text-end">최악일</th>
<th class="text-end" title="높을수록 좋음. 만점 없음. 일평균PnL λ×일표준편차(원).">안정점수 ↑</th>
<th class="text-end" title="높을수록 위험. 0~100. 이 숫자만 믿으면 내일 틀릴 수 있는 정도(추정).">과적합% ↓</th>
@@ -4079,7 +4256,7 @@
</tr>
</thead>
<tbody id="opt_top5_stable_tbody">
<tr><td colspan="11" class="text-muted">완료 후 표시 (구 JSON은 재실행 필요)</td></tr>
<tr><td colspan="12" class="text-muted">완료 후 표시 (구 JSON은 재실행 필요)</td></tr>
</tbody>
</table>
</div>
@@ -4112,7 +4289,7 @@
<div class="card p-3 mb-3">
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-2">
<h6 class="mb-0">최근 잡 <small class="text-muted fw-normal">(8개)</small></h6>
<h6 class="mb-0">최근 잡 <small class="text-muted fw-normal">(최대 40 · 순차 스터디가 잘리지 않게)</small></h6>
<div class="d-flex align-items-center gap-2">
<label class="small text-muted mb-0" for="opt_jobs_sort">정렬</label>
<select id="opt_jobs_sort" class="form-select form-select-sm" style="width:120px" onchange="optunaRefreshJobs()">
@@ -4164,6 +4341,6 @@
</div>
</div>
<script src="{{ url_for('static', filename='js/backtest.js') }}?v=20260823obRenameBuy"></script>
<script src="{{ url_for('static', filename='js/backtest.js') }}?v=20260823obDbTruth"></script>
</body>
</html>