auto: 일일 백업 2026-06-28 02:00

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hyowons
2026-06-28 02:00:01 +09:00
parent 6c89c54fe5
commit 790effdf0e
+23 -18
View File
@@ -672,9 +672,13 @@ def _fetch_quotes_batch(entries: list[dict]) -> list[dict]:
if not miss_codes: if not miss_codes:
return cards return cards
nxt_probe_codes = list(miss_codes)
nxt_probe_indices = list(miss_indices)
# WS 허브 연결 시 — 미스 종목 시세를 허브 메모리(0B 실시간, KRX+NXT 통합)에서 우선 충당. # WS 허브 연결 시 — 미스 종목 시세를 허브 메모리(0B 실시간, KRX+NXT 통합)에서 우선 충당.
# 서버가 동일 렌더러로 OHLC·마크를 그대로 그린다 (마크는 허브 exchange 필드로 판정). # 서버가 동일 렌더러로 OHLC를 그린다. 단, NXT 거래가능 초록점은 허브 exchange가 아니라
# 허브에 없는(아직 미체결) 종목만 ka10095 폴백으로 남긴다. 캐시엔 안 넣음 — 매 페치 라이브 허브 사용. # 아래 ka10095 NX 단독 응답 가능 여부로만 판정한다.
# 허브에 없는(아직 미체결) 종목만 ka10095 AL 폴백으로 남긴다. 캐시엔 안 넣음 — 매 페치 라이브 허브 사용.
hub = _RT_HUB hub = _RT_HUB
if hub is not None and hub.is_connected(): if hub is not None and hub.is_connected():
hub_q = hub.get_quotes(miss_codes) hub_q = hub.get_quotes(miss_codes)
@@ -694,30 +698,32 @@ def _fetch_quotes_batch(entries: list[dict]) -> list[dict]:
out['open'] = q.get('open') or None out['open'] = q.get('open') or None
out['high'] = q.get('high') or None out['high'] = q.get('high') or None
out['low'] = q.get('low') or None out['low'] = q.get('low') or None
is_nxt = (q.get('exchange') or '').upper() == 'NXT' out['_krx_price'] = price
out['_krx_price'] = 0 if is_nxt else price out['_nxt_price'] = 0
out['_nxt_price'] = price if is_nxt else 0
miss_codes, miss_indices = rest_codes, rest_indices miss_codes, miss_indices = rest_codes, rest_indices
if not miss_codes:
return cards
try: batch: dict[str, dict] = {}
batch = kc.get_watchlist_quotes(miss_codes) if miss_codes:
except Exception as ex: try:
sys.stderr.write(f'ka10095 batch 실패, ka10001 fallback: {ex}\n') batch = kc.get_watchlist_quotes(miss_codes)
for idx in miss_indices: except Exception as ex:
cards[idx] = _fetch_quote(entries[idx]) sys.stderr.write(f'ka10095 batch 실패, ka10001 fallback: {ex}\n')
return cards for idx in miss_indices:
cards[idx] = _fetch_quote(entries[idx])
# NXT 비교용 batch — 마크(KRX/NXT) 판정에 사용. 실패해도 fallback X (마크만 KRX로 처리). # NXT 비교용 batch — 마크(KRX/NXT) 판정에 사용. 실패해도 fallback X (마크만 KRX로 처리).
# get_watchlist_quotes는 NXT 단독 suffix를 'NX'로 받는다. 'NXT'는 suffix 없이 KRX/기본값으로 # get_watchlist_quotes는 NXT 단독 suffix를 'NX'로 받는다. 'NXT'는 suffix 없이 KRX/기본값으로
# 떨어져 NXT 미거래 종목도 거래 가능처럼 보이므로 쓰면 안 된다. # 떨어져 NXT 미거래 종목도 거래 가능처럼 보이므로 쓰면 안 된다.
nxt_batch: dict[str, dict] = {} nxt_batch: dict[str, dict] = {}
try: try:
nxt_batch = kc.get_watchlist_quotes(miss_codes, exchange='NX') nxt_batch = kc.get_watchlist_quotes(nxt_probe_codes, exchange='NX')
except Exception as ex: except Exception as ex:
sys.stderr.write(f'ka10095 NXT batch 실패 (마크 판정용): {ex}\n') sys.stderr.write(f'ka10095 NXT batch 실패 (마크 판정용): {ex}\n')
for idx, code in zip(nxt_probe_indices, nxt_probe_codes):
nxt_q = nxt_batch.get(code) if nxt_batch else None
cards[idx]['_nxt_price'] = (nxt_q or {}).get('price', 0) if nxt_q else 0
for idx, code in zip(miss_indices, miss_codes): for idx, code in zip(miss_indices, miss_codes):
q = batch.get(code) q = batch.get(code)
out = cards[idx] out = cards[idx]
@@ -735,8 +741,7 @@ def _fetch_quotes_batch(entries: list[dict]) -> list[dict]:
else: else:
out['quote_error'] = '가격 0' out['quote_error'] = '가격 0'
# 마크 판정: NXT batch 가격 vs 기본 batch 가격 비교. # 마크 판정: NXT batch 가격 vs 기본 batch 가격 비교.
nxt_q = nxt_batch.get(code) if nxt_batch else None nxt_price = out.get('_nxt_price', 0) or 0
nxt_price = (nxt_q or {}).get('price', 0) if nxt_q else 0
out['_krx_price'] = price out['_krx_price'] = price
out['_nxt_price'] = nxt_price out['_nxt_price'] = nxt_price
snap = { snap = {
@@ -6158,7 +6163,7 @@ def _render_watchlist_panel(cards: list[dict]) -> tuple[str, int]:
sections.append(f'<div class="section-label">보유중<span class="count">{len(held)}</span></div>') sections.append(f'<div class="section-label">보유중<span class="count">{len(held)}</span></div>')
sections.append('\n'.join(_render_row(c) for c in held)) sections.append('\n'.join(_render_row(c) for c in held))
if watching: if watching:
sections.append(f'<div class="section-label">매수전<span class="count">{len(watching)}</span></div>') sections.append(f'<div class="section-label">감시<span class="count">{len(watching)}</span></div>')
sections.append('\n'.join(_render_row(c) for c in watching)) sections.append('\n'.join(_render_row(c) for c in watching))
if trash: if trash:
sections.append(f'<div class="section-label trash-label">삭제예정<span class="count">{len(trash)}</span></div>') sections.append(f'<div class="section-label trash-label">삭제예정<span class="count">{len(trash)}</span></div>')