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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hyowons
2026-06-19 02:00:06 +09:00
parent 70e2251995
commit dc537f9925
358 changed files with 6184 additions and 1360 deletions
@@ -13,9 +13,12 @@
from __future__ import annotations
import json
import http.client
import socket
import sys
import threading
import time
import urllib.error
import urllib.request
from datetime import datetime, timezone, timedelta
from pathlib import Path
@@ -48,6 +51,8 @@ TR_MINUTE_CHART = 'ka10080' # 주식분봉차트조회 (1/3/5/10/15/30/45/60분
TR_INVESTOR_FLOW = 'ka10059' # 종목별투자자기관별 (외국인·기관 일별 매매)
STOCK_CODES_CACHE = STATE_DIR / 'stock_codes.json'
_HTTP_TRANSIENT_RETRIES = 2
_HTTP_TRANSIENT_BACKOFF_SEC = 0.8
def load_credentials() -> dict:
@@ -86,13 +91,28 @@ def _http_post_full(url: str, body: dict, headers: dict | None = None) -> tuple[
if headers:
h.update(headers)
req = urllib.request.Request(url, data=data, method='POST', headers=h)
try:
with urllib.request.urlopen(req, timeout=15) as r:
resp_headers = {k.lower(): v for k, v in r.headers.items()}
return json.loads(r.read().decode('utf-8', 'ignore')), resp_headers
except urllib.error.HTTPError as e:
body_text = e.read().decode('utf-8', 'ignore')
raise RuntimeError(f'HTTP {e.code} {url}: {body_text}') from e
transient_errors = (
ConnectionResetError,
TimeoutError,
socket.timeout,
urllib.error.URLError,
http.client.RemoteDisconnected,
http.client.IncompleteRead,
)
for attempt in range(_HTTP_TRANSIENT_RETRIES + 1):
try:
with urllib.request.urlopen(req, timeout=15) as r:
resp_headers = {k.lower(): v for k, v in r.headers.items()}
return json.loads(r.read().decode('utf-8', 'ignore')), resp_headers
except urllib.error.HTTPError as e:
body_text = e.read().decode('utf-8', 'ignore')
raise RuntimeError(f'HTTP {e.code} {url}: {body_text}') from e
except transient_errors:
if attempt >= _HTTP_TRANSIENT_RETRIES:
raise
time.sleep(_HTTP_TRANSIENT_BACKOFF_SEC * (attempt + 1))
raise RuntimeError(f'HTTP POST failed unexpectedly: {url}')
def _token_cache_path(label: str) -> Path: