Files
kis_trader/static/css/backtest.css
Your Name 0ecac7cb95 이번에 들어간 내용
한투 호가 = 2번째 앱키 전용
키 없거나 start 실패 시 메인에 H0STASP0 안 붙임. 운영설정 WS_ORDERBOOK_SAVE_KIS 빨간 danger.

LS RAM 합집합
후보∪보유∪영구∪grace. sync_targets와 split reconcile 둘 다. 틱 DB 영구 게이트는 그대로.

분봉 쓰레기 → 다음 소스 봉 통째
그 분 틱 0건이거나 전부 봉끝 대비 LIVE_FEED_FALLBACK_MAX_AGE_SEC 초과면 구멍. 메인 WS → 2차 → LS → REST → rollup. CANDLE_GARBAGE_FALLBACK 기본 true.

파일: feed_fallback.py(신규), ws_manager.py, kis_ws.py, candle_series.py, bt_candle_source.py, live_config_schema.py, database.py, 스모크, MD 2개.

같은 ws_manager/database/kis_ws/live_config에는 직전 커밋 이후 쌓여 있던 시세 폴백·ENV 키 정리도 같이 들어갔습니다. 파일 단위로 나눌 수 없어서입니다.
2026-08-19 22:11:31 +09:00

306 lines
13 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ── Bootstrap dark-theme 변수 오버라이드 ── */
[data-bs-theme="dark"] {
--bs-body-bg: #0d1117;
--bs-body-color: #e6edf3;
--bs-border-color: #21262d;
--bs-secondary-bg: #161b22;
--bs-tertiary-bg: #161b22;
--bs-table-color: #e6edf3;
--bs-table-bg: transparent;
--bs-table-border-color: #21262d;
--bs-table-hover-bg: rgba(88,166,255,.07);
--bs-table-hover-color: #e6edf3;
--bs-form-control-bg: #21262d;
--bs-form-control-color: #e6edf3;
--bs-input-bg: #21262d;
--bs-input-color: #e6edf3;
--bs-link-color: #58a6ff;
}
:root {
--bg: #0d1117; --surface: #161b22; --border: #21262d;
--text: #e6edf3; --muted: #8b949e; --accent: #58a6ff;
--green: #3fb950; --red: #f85149; --yellow: #d29922; --purple: #bc8cff;
}
* { box-sizing: border-box; }
body { background: var(--bg) !important; color: var(--text) !important;
font-family: 'Segoe UI', sans-serif; font-size: 14px; }
/* 네비 / 탭 */
.navbar-brand { font-weight: 700; letter-spacing: 1px; color: var(--accent) !important; }
.nav-tabs .nav-link { color: var(--muted); border: none; padding: 10px 20px; }
.nav-tabs .nav-link.active { color: var(--accent); border-bottom: 2px solid var(--accent); background: transparent; }
.nav-tabs { border-bottom: 1px solid var(--border); }
/* 카드 */
.card { background: var(--surface) !important; border: 1px solid var(--border) !important; border-radius: 10px; }
.card-title { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 4px; }
.stat-val { font-size: 26px; font-weight: 700; color: var(--text); }
.stat-val.green { color: var(--green) !important; }
.stat-val.red { color: var(--red) !important; }
/* 폼 컨트롤 */
.form-control, .form-select, input[type=number], input[type=date] {
background: #21262d !important; border: 1px solid #30363d !important;
color: var(--text) !important; border-radius: 6px;
}
.form-control:focus, .form-select:focus, input:focus {
background: #21262d !important; color: var(--text) !important;
border-color: var(--accent) !important; box-shadow: none !important;
}
.btn-primary { background: var(--accent) !important; border: none !important; font-weight: 600; color: #0d1117 !important; }
.btn-primary:hover { background: #79b8ff !important; }
label, .form-label { color: var(--muted) !important; font-size: 12px; }
/* 테이블 — Bootstrap dark theme가 대부분 처리, 남은 것만 보정 */
.table { font-size: 12px; }
.table td, .table th { color: var(--text) !important; border-color: var(--border) !important; padding: 6px 10px; }
.table thead th { color: var(--muted) !important; }
.table-responsive { max-height: 420px; overflow-y: auto; }
.table-responsive::-webkit-scrollbar { width: 5px; }
.table-responsive::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
/* 배지 */
.badge-win { background: rgba(63,185,80,.2); color: var(--green) !important; padding: 2px 8px; border-radius: 20px; }
.badge-loss { background: rgba(248,81,73,.2); color: var(--red) !important; padding: 2px 8px; border-radius: 20px; }
.badge-flat { background: rgba(210,153,34,.15); color: var(--yellow) !important; padding: 2px 8px; border-radius: 20px; }
.badge-reason { background: rgba(88,166,255,.12); color: #8b949e !important; padding: 2px 8px; border-radius: 20px; font-size: 11px; white-space: nowrap; }
.trade-sort-bar { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: 12px; }
.trade-sort-bar .trade-sort-label { color: var(--muted); margin-right: 4px; font-weight: 600; }
.trade-sort-btn { font-size: 11px !important; padding: 2px 8px !important; }
.trade-sort-btn.active { background: rgba(88,166,255,.22) !important; border-color: var(--accent) !important; color: var(--accent) !important; }
/* 스피너 */
.spin-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.55);
z-index: 9999; align-items: center; justify-content: center; }
.spin-overlay.show { display: flex; }
.trade-db-chart-overlay {
position: fixed; inset: 0; z-index: 10050;
background: rgba(0,0,0,.65);
align-items: center; justify-content: center;
padding: 16px;
}
.trade-db-chart-panel {
width: min(960px, 96vw);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 14px 16px 12px;
box-shadow: 0 12px 40px rgba(0,0,0,.45);
}
.trade-ext-links a { color: var(--accent); text-decoration: none; }
.trade-ext-links a:hover { text-decoration: underline; }
.spinner { width: 48px; height: 48px; border: 4px solid var(--border);
border-top-color: var(--accent); border-radius: 50%; animation: spin .9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
/* 기타 */
canvas { background: transparent !important; }
.section-title { font-size: 13px; font-weight: 600; color: var(--muted);
text-transform: uppercase; letter-spacing: 1px; margin: 24px 0 12px; }
input[type=radio] { accent-color: var(--accent); }
.ratio-bar { height: 8px; border-radius: 4px; background: var(--border); margin-top: 4px; overflow: hidden; }
.ratio-fill-g { height: 100%; border-radius: 4px 0 0 4px; background: var(--green); display: inline-block; }
.ratio-fill-r { height: 100%; border-radius: 0 4px 4px 0; background: var(--red); display: inline-block; float: right; }
.text-pnl-pos { color: var(--green) !important; }
.text-pnl-neg { color: var(--red) !important; }
/* 보유·실거래 보유중 — 업비트식 틱 플래시 (표시만) */
.live-quote-flash-up {
animation: liveQuoteFlashUp 0.45s ease;
}
.live-quote-flash-down {
animation: liveQuoteFlashDown 0.45s ease;
}
@keyframes liveQuoteFlashUp {
from { background-color: rgba(63, 185, 80, 0.35); }
to { background-color: transparent; }
}
@keyframes liveQuoteFlashDown {
from { background-color: rgba(248, 81, 73, 0.35); }
to { background-color: transparent; }
}
.live-quote-stale { opacity: 0.55; }
/* 파라미터 설명 툴팁 */
.param-help { font-size: 10px; color: var(--muted); margin-top: 2px; }
/* 운영 설정 탭 — 최대 2열, 위에서 아래로 읽기 */
.lc-field-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px 20px;
max-width: 920px;
}
@media (max-width: 700px) {
.lc-field-grid { grid-template-columns: 1fr; }
}
.lc-field {
min-width: 0;
}
.lc-field input.form-control {
max-width: 100%;
}
.lc-field-label { font-size: 12px; color: var(--muted); display: block; margin-bottom: 2px; line-height: 1.35; }
.lc-field-hint { font-size: 10px; color: var(--muted); margin-top: 2px; line-height: 1.35; }
.lc-field-tbl { font-size: 10px; color: #58a6ff; margin-left: 4px; }
.lc-field-key { font-size: 10px; color: #8b949e; margin-left: 4px; font-weight: normal; }
/* 키/테이블 태그: 평소엔 숨기고 호버 시만 — 라벨·입력에 시선 집중 */
.lc-field-key,
.lc-field-tbl {
opacity: 0.35;
transition: opacity 0.12s ease;
}
.lc-field:hover .lc-field-key,
.lc-field:hover .lc-field-tbl,
.lc-field:focus-within .lc-field-key,
.lc-field:focus-within .lc-field-tbl {
opacity: 1;
}
/* 카테고리 안 전략·주제 구역 */
.lc-strat-stack {
display: flex;
flex-direction: column;
gap: 12px;
max-width: 960px;
}
.lc-strat-block {
border: 1px solid var(--border);
border-left-width: 4px;
border-radius: 8px;
padding: 10px 12px 12px;
background: rgba(22, 27, 34, 0.55);
}
.lc-strat-head {
display: flex;
align-items: baseline;
gap: 8px;
margin-bottom: 8px;
padding-bottom: 5px;
border-bottom: 1px solid var(--border);
font-size: 13px;
font-weight: 650;
letter-spacing: 0.02em;
color: var(--text);
}
.lc-strat-head .lc-strat-sid {
font-size: 11px;
font-weight: 500;
color: var(--muted);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.lc-topic-head {
font-size: 12px;
font-weight: 600;
color: var(--muted);
margin: 4px 0 8px;
padding-left: 2px;
}
.lc-strat-common { border-left-color: #6e7681; }
.lc-strat-short { border-left-color: #39d2c0; }
.lc-strat-momentum { border-left-color: #d29922; }
.lc-strat-breakout { border-left-color: #3fb950; }
.lc-strat-updow { border-left-color: #58a6ff; }
.lc-strat-scalp { border-left-color: #8b949e; }
.lc-strat-range_break { border-left-color: #79c0ff; }
.lc-strat-dbband { border-left-color: #f0883e; }
.lc-strat-other { border-left-color: #bc8cff; }
.lc-strat-topic { border-left-color: #6e7681; }
.lc-strat-short .lc-strat-head { color: #39d2c0; }
.lc-strat-momentum .lc-strat-head { color: #d29922; }
.lc-strat-breakout .lc-strat-head { color: #3fb950; }
.lc-strat-updow .lc-strat-head { color: #58a6ff; }
.lc-strat-scalp .lc-strat-head { color: #c9d1d9; }
.lc-strat-common .lc-strat-head { color: #8b949e; }
/* OHLC 폴백 — 정합 위험 (절대규칙: 틱 모드에서 숫자 변조 금지). UI만 경고 표시 */
.lc-field-danger,
.bt-ohlc-fallback-danger {
background: rgba(248, 81, 73, 0.18) !important;
border: 1px solid rgba(248, 81, 73, 0.65) !important;
border-radius: 6px;
padding: 6px 10px !important;
margin: 4px 0;
}
.lc-field-danger .lc-field-label,
.bt-ohlc-fallback-danger label,
.bt-ohlc-fallback-danger .form-check-label {
color: #ff7b72 !important;
font-weight: 600;
}
.lc-field-danger .lc-field-hint,
.bt-ohlc-fallback-danger .bt-ohlc-fallback-warn {
color: #ffa198 !important;
font-size: 11px;
}
.ob-stamp { font-size: 10px; line-height: 1.35; }
.ob-stamp-danger {
color: #f85149 !important;
font-weight: 700;
}
.ob-metric-ok { color: var(--green) !important; font-weight: 600; }
.ob-metric-bad { color: var(--red) !important; font-weight: 600; }
/* 거래표 — 정합 예외 경로(OHLC폴백·틱공백EOD 등) */
tr.trade-row-parity-ex {
background: rgba(248, 81, 73, 0.16) !important;
position: relative;
}
tr.trade-row-parity-ex td {
border-color: rgba(248, 81, 73, 0.35) !important;
}
td.trade-name-cell { position: relative; padding-top: 14px !important; }
.parity-cause-corner {
position: absolute;
top: 2px;
right: 4px;
font-size: 9px;
font-weight: 700;
color: #ff7b72;
background: rgba(248, 81, 73, 0.25);
border: 1px solid rgba(248, 81, 73, 0.55);
border-radius: 3px;
padding: 0 4px;
line-height: 1.4;
white-space: nowrap;
max-width: 110px;
overflow: hidden;
text-overflow: ellipsis;
}
/* Optuna 전역/탭 프로그레스 */
.optuna-nav-bar { min-width: 180px; }
.optuna-nav-bar.d-none { display: none !important; }
.optuna-prog-track {
height: 6px; border-radius: 4px; background: var(--border); overflow: hidden;
}
.optuna-prog-fill {
height: 100%; border-radius: 4px; background: var(--accent);
transition: width .35s ease;
}
.optuna-prog-fill.is-done { background: var(--green); }
.optuna-prog-fill.is-error { background: var(--red); }
.optuna-prog-fill.is-post { background: #d29922; }
/* 오늘 운영 — 조건검색 이력(키움/LS) */
.dash-univ-table .dash-univ-src-kiwoom { color: #58a6ff; font-weight: 600; }
.dash-univ-table .dash-univ-src-kis { color: #3fb950; font-weight: 600; }
.dash-univ-table .dash-univ-src-ls { color: #bc8cff; font-weight: 600; }
.dash-univ-table tr.dash-univ-group-start td { border-top: 2px solid var(--border); }
.dash-univ-table tr.dash-univ-stale td { opacity: 0.85; }
/* 전략×2행(10행)이 잘리지 않도록 — 전역 .table-responsive 420px 오버라이드 */
.dash-univ-wrap.table-responsive { max-height: none; overflow-y: visible; }
/* 오늘 운영 — 전략별 키움/LS 매매 두 줄 */
.dash-ops-table .dash-univ-src-kiwoom { color: #58a6ff; font-weight: 600; }
.dash-ops-table .dash-univ-src-kis { color: #3fb950; font-weight: 600; }
.dash-ops-table .dash-univ-src-ls { color: #bc8cff; font-weight: 600; }
.dash-ops-table tr.dash-broker-head td {
background: rgba(88, 166, 255, 0.08);
font-weight: 700;
border-top: 2px solid var(--border);
color: var(--text);
}
.dash-ops-table tr.dash-broker-head.dash-broker-kis td { background: rgba(63, 185, 80, 0.08); }
.dash-ops-table tr.dash-ops-group-start td { border-top: 2px solid var(--border); }
.dash-ops-table tr.dash-ops-inactive td { opacity: 0.55; }