auto: 일일 백업 2026-06-18 02:00
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1945,8 +1945,13 @@ def render_html(view: str | None = None, day_date: str | None = None,
|
||||
|
||||
# 상태 그룹 = 전체 전략(메인+변이) 합산 — 한 계좌라도 보유→보유 / 매수직전→매수 / 대기→대기.
|
||||
# 아무 계좌도 안 보면 기준전략(M) 판정. 카드 체크리스트·사유는 기준전략 시각 유지(합산 정보는 사유 앞에 표시).
|
||||
by_state = {}
|
||||
excluded = [] # 매매 안 하는 종목(ETF/ETN 등) → 하단 제외목록
|
||||
# 출처(origin) 맵 — auto(자동편입) 종목은 별도 섹션으로 분리
|
||||
_wl0 = umod._load_watchlist()
|
||||
origin_map = {c: e.get('origin', '') for c, e in _wl0.items()}
|
||||
|
||||
by_state = {} # 내 종목(워치·관심·보유·수동)
|
||||
by_state_auto = {} # 자동편입(auto) — 하단 별도
|
||||
excluded = [] # 매매 안 하는 종목(ETF/ETN 등) → 하단 제외목록
|
||||
for d in decisions:
|
||||
if umod.is_etf(d.get('name', '')):
|
||||
excluded.append(d)
|
||||
@@ -1969,18 +1974,30 @@ def render_html(view: str | None = None, day_date: str | None = None,
|
||||
elif watch_n.get(code):
|
||||
bits.append(f'{watch_n[code]}개 전략 매수 대기')
|
||||
d = {**d, 'reason': ' · '.join(bits) + f' — {view_label}: {d.get("reason", "")}'}
|
||||
by_state.setdefault(st, []).append(d)
|
||||
# 출처대로만 분류 — auto(자동편입)는 변이가 페이퍼로 보유 중이어도 자동편입 탭에 (보유는 그 안 '보유' 그룹에 표시)
|
||||
tgt = by_state_auto if origin_map.get(code) == 'auto' else by_state
|
||||
tgt.setdefault(st, []).append(d)
|
||||
|
||||
judge_html = ''
|
||||
for st in STATE_ORDER:
|
||||
items = by_state.get(st, [])
|
||||
if not items:
|
||||
continue
|
||||
label, color = STATE_BADGE.get(st, (st, '#888'))
|
||||
judge_html += f'<h3 class="grp"><span class="badge" style="background:{color}">{label}</span> {len(items)}</h3>'
|
||||
for d in items:
|
||||
judge_html += _decision_card(d, watch_n.get(d.get('code'), 0), held_n.get(d.get('code'), 0),
|
||||
_acc_detail(d.get('code')), buy_n.get(d.get('code'), 0))
|
||||
def _state_blocks(bucket):
|
||||
# 상태별 접이식 — 액션 상태(보유·추격·매수·매도·대기)는 펼침, 제외·데이터없음은 접힘(노이즈)
|
||||
_open = {'HOLD', 'ADD', 'BUY', 'SELL', 'WAIT'}
|
||||
out = ''
|
||||
for st in STATE_ORDER:
|
||||
items = bucket.get(st, [])
|
||||
if not items:
|
||||
continue
|
||||
label, color = STATE_BADGE.get(st, (st, '#888'))
|
||||
cards = ''.join(_decision_card(d, watch_n.get(d.get('code'), 0), held_n.get(d.get('code'), 0),
|
||||
_acc_detail(d.get('code')), buy_n.get(d.get('code'), 0))
|
||||
for d in items)
|
||||
op = ' open' if st in _open else ''
|
||||
out += (f'<details class="row state-grp"{op}><summary>'
|
||||
f'<span class="badge" style="background:{color}">{label}</span> {len(items)}</summary>'
|
||||
f'<div class="body">{cards}</div></details>')
|
||||
return out
|
||||
|
||||
mine_n = sum(len(v) for v in by_state.values())
|
||||
judge_html = _state_blocks(by_state)
|
||||
|
||||
# 관찰목록 중 아직 스캔 안 된(방금 추가 등) 종목 → '스캔 대기'로 즉시 표시
|
||||
wl = umod._load_watchlist()
|
||||
@@ -2000,7 +2017,12 @@ def render_html(view: str | None = None, day_date: str | None = None,
|
||||
f'<span class="price muted">다음 스캔 대기</span>{del_btn}</div></div>'
|
||||
)
|
||||
|
||||
# 매매 안 하는 종목(ETF/ETN 등) → 하단 접이식 제외목록
|
||||
# 자동편입(auto) 종목 → 상단 서브탭 '자동편입'으로 분리 (별도 HTML)
|
||||
auto_n = sum(len(v) for v in by_state_auto.values())
|
||||
judge_html_auto = (_state_blocks(by_state_auto) if auto_n else
|
||||
'<div class="empty">자동편입된 종목이 없어요 (매일 장 마감 후 키움 순위정보로 편입)</div>')
|
||||
|
||||
# 매매 안 하는 종목(ETF/ETN 등) → 하단 접이식 제외목록 (내 종목 쪽에 둠)
|
||||
if excluded:
|
||||
judge_html += (
|
||||
f'<details class="row" style="margin-top:16px"><summary>'
|
||||
@@ -2058,6 +2080,12 @@ h1{{font-size:16px;margin:0 0 2px}} .sub{{color:#9ca3af;font-size:11px;margin:0
|
||||
.kpi-sub{{color:#6b7280;font-size:10px;margin-top:2px}}
|
||||
h2{{font-size:15px;margin:22px 0 8px;border-bottom:1px solid #1f2937;padding-bottom:6px}}
|
||||
h3.grp{{font-size:13px;margin:16px 0 8px;color:#cbd5e1}}
|
||||
.state-grp{{margin:6px 0;border-top:none}}
|
||||
.state-grp>summary{{padding:8px 2px;font-size:13px;font-weight:600;list-style:none;display:flex;align-items:center;gap:6px}}
|
||||
.state-grp>summary::-webkit-details-marker{{display:none}}
|
||||
.state-grp>summary::before{{content:'▸';color:#6b7280;font-size:11px}}
|
||||
.state-grp[open]>summary::before{{content:'▾'}}
|
||||
.state-grp>.body{{padding:2px 0 4px}}
|
||||
.badge{{color:#fff;border-radius:6px;padding:1px 8px;font-size:12px;font-weight:600}}
|
||||
.row{{background:#141b24;border:1px solid #1f2937;border-radius:10px;margin-bottom:8px;overflow:hidden}}
|
||||
.row>summary{{list-style:none;cursor:pointer;padding:10px 12px;display:flex;flex-wrap:nowrap;align-items:center;gap:8px;font-size:13px;overflow:hidden}}
|
||||
@@ -2156,7 +2184,7 @@ h3.grp{{font-size:13px;margin:16px 0 8px;color:#cbd5e1}}
|
||||
#chk-toast .ct-t{{font-size:15px;font-weight:700;color:#fff;margin-bottom:8px}}
|
||||
#chk-toast .ct-d{{font-size:13px;line-height:1.6;color:#cbd5e1;white-space:pre-line}}
|
||||
#chk-toast .ct-x{{margin-top:14px;width:100%;background:#1e3a8a;color:#fff;border:1px solid #2952cc;border-radius:9px;padding:10px;font-size:13px;font-weight:600;cursor:pointer}}
|
||||
input[name="tab"],input[name="subtab"]{{display:none}}
|
||||
input[name="tab"],input[name="subtab"],input[name="wsub"]{{display:none}}
|
||||
.hd{{position:sticky;top:0;z-index:30;background:#0b0f14;padding:6px 0;border-bottom:1px solid #161e29}}
|
||||
#page.dragging .hd{{position:static}}
|
||||
.tabs{{display:flex;gap:6px;margin:0}}
|
||||
@@ -2169,6 +2197,9 @@ input[name="tab"],input[name="subtab"]{{display:none}}
|
||||
#sub-cmp:checked~#p-lab label[for="sub-cmp"],#sub-bt:checked~#p-lab label[for="sub-bt"]{{background:#15406e;color:#fff;border-color:#2952cc}}
|
||||
#p-lab .subpanel{{display:none}}
|
||||
#p-lab .subpanel h2{{margin-top:4px}}
|
||||
#p-watch .subpanel{{display:none}}
|
||||
#wsub-mine:checked~#p-watch #pw-mine,#wsub-auto:checked~#p-watch #pw-auto{{display:block}}
|
||||
#wsub-mine:checked~#p-watch label[for="wsub-mine"],#wsub-auto:checked~#p-watch label[for="wsub-auto"]{{background:#15406e;color:#fff;border-color:#2952cc}}
|
||||
.sortbar{{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 12px}}
|
||||
.sortbar button{{background:#141b24;border:1px solid #1f2937;border-radius:7px;color:#9ca3af;font-size:12px;font-weight:600;padding:7px 10px;cursor:pointer;font-variant-numeric:tabular-nums}}
|
||||
.sortbar button.active{{background:#15406e;color:#fff;border-color:#2952cc}}
|
||||
@@ -2239,6 +2270,8 @@ input[name="tab"],input[name="subtab"]{{display:none}}
|
||||
<input type="radio" name="subtab" id="sub-cmp"{_ck('sub-cmp')}>
|
||||
<input type="radio" name="subtab" id="sub-bt"{_ck('sub-bt')}>
|
||||
<input type="radio" name="subtab" id="sub-day"{_ck('sub-day')}>
|
||||
<input type="radio" name="wsub" id="wsub-mine" checked>
|
||||
<input type="radio" name="wsub" id="wsub-auto">
|
||||
<header class="hd">
|
||||
<h1> 레이 자동매매 시뮬레이션 <span id="health" class="hdot {hcls}" data-detail="{_esc(hdetail)}" role="button" tabindex="0" title="시뮬 상태 보기"></span></h1>
|
||||
<div class=sub>마지막 스캔 {scanned or '—'} · 시장 {mkt_h}</div>
|
||||
@@ -2253,7 +2286,11 @@ input[name="tab"],input[name="subtab"]{{display:none}}
|
||||
<div class="sub">상태 그룹은 전체 전략(메인+변이) 합산 — 한 전략이라도 보유/매수대기면 그 그룹으로 · 배지: 🟢매수준비 · 🟡대기 · 🛒보유 전략 수 (탭하면 어떤 전략인지)</div>
|
||||
<div class="sub">종목명 일부만 입력해도 후보가 떠요 · 후보를 누르면 추가 · 자산(비하이브 워치·관심·보유)은 자동 편입 · 삭제는 카드의 ✕ (수동만) · 보유 중이면 삭제해도 청산까지 추적</div>
|
||||
{view_sel}
|
||||
{judge_html}
|
||||
<nav class="subtabs" id="wsubtabs">
|
||||
<label for="wsub-mine">📌 내 종목 ({mine_n})</label>
|
||||
<label for="wsub-auto">🤖 자동편입 ({auto_n})</label></nav>
|
||||
<div class="subpanel" id="pw-mine">{judge_html}</div>
|
||||
<div class="subpanel" id="pw-auto">{judge_html_auto}</div>
|
||||
</section>
|
||||
<section class="panel" id="p-market">
|
||||
<h2>시장 동향</h2>{market_html}
|
||||
|
||||
Reference in New Issue
Block a user