auto: 일일 백업 2026-09-11 02:00

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hyowons
2026-09-11 02:00:03 +09:00
parent 92d0d96368
commit b4f510d1ad
188 changed files with 4782 additions and 3353 deletions
@@ -814,11 +814,30 @@ def get_watchlist_quotes(codes: list[str], account_label: str | None = None,
for i in range(0, len(clean_codes), WATCHLIST_BATCH_MAX):
chunk = clean_codes[i:i + WATCHLIST_BATCH_MAX]
stk_cd_param = '|'.join(f'{c}{suffix}' for c in chunk)
resp = _call(label, TR_WATCHLIST_INFO, {'stk_cd': stk_cd_param}, endpoint=ENDPOINT_STKINFO)
out.update(_parse_watchlist_rows(resp))
out.update(_parse_watchlist_rows(_watchlist_batch_call(label, stk_cd_param)))
return out
def _watchlist_batch_call(label: str, stk_cd_param: str) -> dict:
"""ka10095 1콜 + 429 재시도.
behive_web 은 패널 렌더 1회에 이 TR 을 최대 7콜(보유 KRX/NX/AL · 워치리스트 · NXT 마크
판정 · phantom · 미보유 미체결) 병렬 발사하는데 키움 유량은 초당 5건이라 일부가 429 로
튕긴다. ⚠️ 튕긴 콜은 예외가 호출측에서 로그만 남고 삼켜져 **그 조각이 화면에서 조용히
빈다** — 실측 `NXT positions price fetch failed` 25건, 그때마다 보유종목 NXT 컬럼이
통째로 사라진다(NXT 실거래가 있는 삼성전자·SK하이닉스만 눈에 띈다).
⚠️ ka10001·ka10004 처럼 `_QUOTE_GATE` 로 직렬화하면 7콜이 줄서서 렌더가 1초 이상
느려지므로, 정상 경로는 병렬 그대로 두고 튕긴 콜만 백오프 후 1회 다시 친다.
"""
try:
return _call(label, TR_WATCHLIST_INFO, {'stk_cd': stk_cd_param}, endpoint=ENDPOINT_STKINFO)
except RuntimeError as e:
if not _is_quote_rate_limit(str(e)):
raise
time.sleep(_QUOTE_BACKOFF_SEC)
return _call(label, TR_WATCHLIST_INFO, {'stk_cd': stk_cd_param}, endpoint=ENDPOINT_STKINFO)
def _parse_watchlist_rows(resp: dict) -> dict[str, dict]:
out: dict[str, dict] = {}
for row in resp.get('atn_stk_infr') or []:
@@ -1363,6 +1382,22 @@ def main():
print(f"{info['name']} ({info['code']}) · {info.get('market','?')}")
return 0
if cmd == 'refresh-codes':
# --if-stale N: 캐시가 N일보다 최신이면 건너뛴다(정기 실행용).
# ⚠️ 이 캐시의 nxt_enable 은 자산웹의 'NXT 거래 종목' 판정 근거다(behive_web._is_nxt_traded).
# 낡으면 새로 NXT 편입된 종목을 놓쳐 🔥 배지·거래가능 초록점이 틀어진다(금액엔 영향 없음).
# lazy 갱신은 '모르는 종목명 검색' 때만 돌아, 알던 종목만 다루면 영영 안 바뀐다.
stale_days = 0.0
if '--if-stale' in sys.argv:
try:
stale_days = float(sys.argv[sys.argv.index('--if-stale') + 1])
except (IndexError, ValueError):
print('usage: refresh-codes [--if-stale <days>]', file=sys.stderr)
return 2
if stale_days > 0 and STOCK_CODES_CACHE.exists():
age_days = (time.time() - STOCK_CODES_CACHE.stat().st_mtime) / 86400
if age_days < stale_days:
print(f'종목코드 캐시 {age_days:.1f}일 — {stale_days:g}일 미만이라 갱신 생략')
return 0
stocks = refresh_stock_codes()
print(f"종목코드 캐시 갱신 완료: {len(stocks)}종목 → {STOCK_CODES_CACHE}")
return 0