[2026-05-19] KIS API TR_ID 수정 — 외국인/기관 수급·업종 지수 API 교체

- get_foreign_institution_rank: FHKST04430000(없는 서비스) → FHPTJ04400000(foreign-institution-total)
- get_sector_trend: FHKST03010100(일봉 오용) → FHPUP02100000(inquire-index-price) 15개 업종 다중 호출
- 두 함수 모두 실거래 API 테스트 통과 확인

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 08:59:35 +09:00
parent 92fec17e0e
commit 60eda5a5ee
+65 -40
View File
@@ -413,61 +413,86 @@ class KISClient:
return result return result
async def get_foreign_institution_rank(self, top_n: int = 30) -> Dict: async def get_foreign_institution_rank(self, top_n: int = 30) -> Dict:
"""외국인/기관 순매수 상위 (AI 판단용)""" """외국인/기관 순매수 상위 (AI 판단용) — FHPTJ04400000"""
data = await self._request( data = await self._request(
method = "GET", method = "GET",
path = "/uapi/domestic-stock/v1/quotations/inquire-investor", path = "/uapi/domestic-stock/v1/quotations/foreign-institution-total",
tr_id = "FHKST04430000", tr_id = "FHPTJ04400000",
params = { params = {
"FID_COND_MRKT_DIV_CODE": "J", "FID_COND_MRKT_DIV_CODE" : "J",
"FID_INPUT_ISCD" : "0000", "FID_COND_SCR_DIV_CODE" : "20171",
"FID_INPUT_DATE_1" : "", "FID_INPUT_ISCD" : "0000",
"FID_INPUT_DATE_2" : "", "FID_DIV_CLS_CODE" : "0",
"FID_PERIOD_DIV_CODE" : "D", "FID_BLNG_CLS_CODE" : "0",
"FID_TRGT_CLS_CODE" : "111111111",
"FID_TRGT_EXLS_CLS_CODE" : "000000",
"FID_INPUT_PRICE_1" : "",
"FID_INPUT_PRICE_2" : "",
"FID_VOL_CNT" : "",
"FID_INPUT_DATE_1" : "",
"FID_RANK_SORT_CLS_CODE" : "1", # 순매수량 기준 정렬
"FID_ETC_CLS_CODE" : "0",
} }
) )
foreign = [] foreign = []
institution = [] institution = []
for row in data.get("output", [])[:top_n]: for row in data.get("output", []):
entry = { ticker = row.get("mksc_shrn_iscd", "")
"ticker": row.get("mksc_shrn_iscd", ""), name = row.get("hts_kor_isnm", "")
"name" : row.get("hts_kor_isnm", ""), try:
"amount": int(row.get("frgn_ntby_qty", "0")), frgn_qty = int(row.get("frgn_ntby_qty") or 0)
} except (ValueError, TypeError):
foreign.append(entry) frgn_qty = 0
entry2 = { try:
"ticker": row.get("mksc_shrn_iscd", ""), orgn_qty = int(row.get("orgn_ntby_qty") or 0)
"name" : row.get("hts_kor_isnm", ""), except (ValueError, TypeError):
"amount": int(row.get("orgn_ntby_qty", "0")), orgn_qty = 0
} foreign.append({"ticker": ticker, "name": name, "amount": frgn_qty})
institution.append(entry2) institution.append({"ticker": ticker, "name": name, "amount": orgn_qty})
return { return {
"foreign" : sorted(foreign, key=lambda x: x["amount"], reverse=True)[:top_n], "foreign" : sorted(foreign, key=lambda x: x["amount"], reverse=True)[:top_n],
"institution": sorted(institution, key=lambda x: x["amount"], reverse=True)[:top_n], "institution": sorted(institution, key=lambda x: x["amount"], reverse=True)[:top_n],
} }
# 업종코드 → 업종명 (KOSPI 주요 업종)
_SECTOR_CODES = [
("0001", "KOSPI"),
("0028", "반도체"),
("0150", "2차전지"),
("0018", "전기전자"),
("0011", "의약품"),
("0010", "화학"),
("0016", "철강금속"),
("0017", "기계"),
("0006", "건설업"),
("0027", "금융업"),
("0026", "통신업"),
("0020", "운수창고"),
("0021", "유통업"),
("0009", "음식료품"),
("0024", "전기가스업"),
]
async def get_sector_trend(self) -> list: async def get_sector_trend(self) -> list:
"""업종별 등락률 (AI 판단용)""" """업종별 등락률 (AI 판단용) — FHPUP02100000 다중 호출"""
data = await self._request(
method = "GET",
path = "/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice",
tr_id = "FHKST03010100",
params = {
"FID_COND_MRKT_DIV_CODE": "U", # 업종
"FID_INPUT_ISCD" : "0001",
"FID_INPUT_DATE_1" : "",
"FID_INPUT_DATE_2" : "",
"FID_PERIOD_DIV_CODE" : "D",
"FID_ORG_ADJ_PRC" : "0",
}
)
result = [] result = []
for row in data.get("output1", []): for code, name in self._SECTOR_CODES:
result.append({ try:
"sector" : row.get("hts_kor_isnm", ""), data = await self._request(
"change_pct": float(row.get("prdy_ctrt", "0")), method = "GET",
}) path = "/uapi/domestic-stock/v1/quotations/inquire-index-price",
tr_id = "FHPUP02100000",
params = {
"FID_COND_MRKT_DIV_CODE": "U",
"FID_INPUT_ISCD" : code,
}
)
o = data.get("output", {})
change_pct = float(o.get("bstp_nmix_prdy_ctrt") or 0)
result.append({"sector": name, "change_pct": change_pct})
except Exception as e:
logger.warning(f"업종 지수 조회 실패 [{name}/{code}]: {e}")
return result return result