ls kiwoom 구독 히스토리 모두 적재
This commit is contained in:
@@ -107,26 +107,24 @@ def load_ls_creds(*, use_mock: bool) -> tuple[str, str]:
|
||||
db.close()
|
||||
|
||||
|
||||
def fetch_access_token(app_key: str, app_secret: str, timeout: float = 15.0) -> str:
|
||||
url = f"{LS_REST_BASE}/oauth2/token"
|
||||
resp = requests.post(
|
||||
url,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||
data={
|
||||
"grant_type": "client_credentials",
|
||||
"appkey": app_key,
|
||||
"appsecretkey": app_secret,
|
||||
"scope": "oob",
|
||||
},
|
||||
def fetch_access_token(
|
||||
app_key: str,
|
||||
app_secret: str,
|
||||
timeout: float = 15.0,
|
||||
*,
|
||||
force: bool = False,
|
||||
reason: str = "",
|
||||
) -> str:
|
||||
"""LS ``/oauth2/token`` — expires_in 캐시 공유 (강제 연타 발급 금지)."""
|
||||
from kis_trader.network.ls_token import fetch_ls_access_token
|
||||
|
||||
return fetch_ls_access_token(
|
||||
app_key,
|
||||
app_secret,
|
||||
timeout=timeout,
|
||||
force=force,
|
||||
reason=reason or "ls_condition",
|
||||
)
|
||||
if resp.status_code >= 400:
|
||||
raise RuntimeError(f"token HTTP {resp.status_code}: {resp.text[:400]}")
|
||||
body = resp.json()
|
||||
token = body.get("access_token") or body.get("accesstoken")
|
||||
if not token:
|
||||
raise RuntimeError(f"no access_token: {body}")
|
||||
return str(token)
|
||||
|
||||
|
||||
def _rest_headers(token: str, tr_cd: str, *, tr_cont: str = "N", tr_cont_key: str = "") -> dict:
|
||||
@@ -172,6 +170,22 @@ def call_item_search(
|
||||
)
|
||||
if resp.status_code >= 400:
|
||||
logger.error("REST %s fail: %s", tr_cd, text[:500])
|
||||
try:
|
||||
from kis_trader.network.ls_token import is_ls_auth_error
|
||||
if is_ls_auth_error(
|
||||
rsp_cd=data.get("rsp_cd") if isinstance(data, dict) else None,
|
||||
rsp_msg=data.get("rsp_msg") if isinstance(data, dict) else None,
|
||||
http_status=resp.status_code,
|
||||
text=text,
|
||||
):
|
||||
raise RuntimeError(
|
||||
f"LS {tr_cd} auth "
|
||||
f"{(data or {}).get('rsp_cd')}: {(data or {}).get('rsp_msg')}"
|
||||
)
|
||||
except RuntimeError:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
return data, rh
|
||||
|
||||
|
||||
@@ -222,6 +236,26 @@ def t1866_list_conditions(
|
||||
data.get("rsp_msg"),
|
||||
json.dumps(data, ensure_ascii=False)[:400],
|
||||
)
|
||||
# 인증 오류는 빈 목록으로 넘기지 않음 — rematch 가 토큰 갱신하도록 raise
|
||||
try:
|
||||
from kis_trader.network.ls_token import is_ls_auth_error
|
||||
except Exception:
|
||||
is_ls_auth_error = None # type: ignore
|
||||
if is_ls_auth_error is not None and is_ls_auth_error(
|
||||
rsp_cd=data.get("rsp_cd"),
|
||||
rsp_msg=data.get("rsp_msg"),
|
||||
http_status=resp.status_code,
|
||||
text=resp.text,
|
||||
):
|
||||
raise RuntimeError(
|
||||
f"LS t1866 auth {data.get('rsp_cd')}: {data.get('rsp_msg')}"
|
||||
)
|
||||
if resp.status_code >= 400:
|
||||
# GW 라우팅 등 — 빈 목록으로 '조건 소실' 오판 금지
|
||||
raise RuntimeError(
|
||||
f"LS t1866 HTTP={resp.status_code} "
|
||||
f"rsp_cd={data.get('rsp_cd')} rsp_msg={data.get('rsp_msg')}"
|
||||
)
|
||||
rows = data.get("t1866OutBlock1") or []
|
||||
if isinstance(rows, dict):
|
||||
rows = [rows]
|
||||
|
||||
Reference in New Issue
Block a user