KIS 장마감 예상체결가 FHKST117300C0 — 함정 5가지
한국투자증권 KIS API의 국내주식 장마감 예상체결가는 GET /uapi/domestic-stock/v1/quotations/exp-closing-price에 tr_id FHKST117300C0으로 호출하고, 필수 파라미터 5개 중 FID_COND_SCR_DIV_CODE=11173은 고정입니다. 종가 단일가 구간에 상한가·하한가로 마감할 것으로 예상되는 종목 목록을 한 번에 받는 용도인데, 응답 9필드에 '예상체결가'라는 이름의 필드가 없고(stck_prpr = 주식 현재가), 연속조회를 하지 않으며, 정렬 기준인 '직전 가격'이 응답에 없습니다. 공식 저장소 기준 모의투자 제공 칸은 비어 있어 실전 앱키 전용입니다.
목차
- 스펙 한 장
- 요청 파라미터 5개 — 출처마다 다른 설명
- 요청·응답 실물
- 함정 5가지
- 봇에서 쓰는 법 — 종가 주문 직전 점검
- 자주 묻는 질문
한 종목의 예상체결가는 허브 글 KIS API 호가 조회 FHKST01010200의 output2(antc_cnpr·antc_vol)로 받습니다. 이 글은 반대 방향 — "지금 종가 단일가에서 상한가·하한가로 끝날 것 같은 종목이 시장 전체에 뭐가 있나"를 받는 순위형 API 하나만 떼어 냅니다. 출처는 한국투자증권 공식 GitHub 저장소 koreainvestment/open-trading-api의 examples_llm/domestic_stock/exp_closing_price/(exp_closing_price.py·chk_exp_closing_price.py), examples_user/domestic_stock/domestic_stock_functions.py, MCP/KIS Code Assistant MCP/data.csv, legacy/README.md·legacy/postman/README.md, 실전계좌_POSTMAN_샘플코드_v2.6.json입니다(2026-10-10 zipball 기준).
1. 스펙 한 장
| 항목 | 값 |
|---|---|
| API명 | [국내주식] 기본시세 > 국내주식 장마감 예상체결가 [국내주식-120] |
| HTS 화면 | eFriend Plus [0183] 장마감 예상체결가 |
| 메서드 · URL | GET /uapi/domestic-stock/v1/quotations/exp-closing-price |
| tr_id | FHKST117300C0 (실전) |
| 헤더 | authorization(Bearer access_token) · appkey · appsecret · tr_id · custtype=P |
| 모의투자 | legacy/README.md 제공 칸 공란 |
| 실전투자 | legacy/postman/README.md ⭕ · Postman v2.6 "J_국내주식 장마감 예상체결가" |
| 연속조회 | 없음 — 공식 함수가 tr_cont 자리에 "" 고정, 재귀 호출 없음 |
| 응답 | output 배열 1개, 행당 9필드 |
같은 '예상체결' 계열 API가 저장소에 여럿 있어 헷갈리기 쉽습니다. 용도별로 나누면 이렇습니다.
| 용도 | API | tr_id |
|---|---|---|
| 종목 1개의 지금 예상체결가 | 주식현재가 호가/예상체결 (output2) | FHKST01010200 |
| 종목 1개의 예상체결가 시간대별 흐름 | 국내주식 예상체결가 추이 | FHPST01810000 |
| 예상체결 기준 등락률 순위 | 국내주식 예상체결 상승/하락상위 | FHPST01820000 |
| 장마감 상·하한가 예상 종목 목록 | 국내주식 장마감 예상체결가 | FHKST117300C0 |
| 예상체결 실시간 푸시 | 국내주식 실시간예상체결 (KRX/NXT/통합) | H0STANC0 · H0NXANC0 · H0UNANC0 |
legacy/README.md에서 모의 칸에 ⭕가 있는 것은 호가/예상체결(FHKST01010200)뿐이고 나머지 예상체결 계열은 공란이니, 모의 서버 openapivts에서 개발하던 봇이라면 시세 조회만 실전 키로 돌리는 구성이 필요합니다. 막히는 목록은 KIS 모의투자 한계에 있습니다.
2. 요청 파라미터 5개 — 출처마다 다른 설명
공식 함수 exp_closing_price()는 다섯 개 모두 빈 문자열이면 호출 전에 ValueError로 멈춥니다. 설명은 출처마다 조금씩 다릅니다.
| 파라미터 | 함수 주석 · data.csv | Postman v2.6 |
|---|---|---|
FID_COND_MRKT_DIV_CODE | 조건시장분류코드 (J:주식) | 시장 분류 코드 (J : 주식) |
FID_INPUT_ISCD | 0000:전체, 0001:거래소, 1001:코스닥, 2001:코스피200, 4001: KRX100 | 업종코드 '0000':전체, '0001':코스피, '1001':코스닥, '2001':코스피200, '4001':KRX100 |
FID_RANK_SORT_CLS_CODE | 0:전체, 1:상한가마감예상, 2:하한가마감예상, 3:직전대비상승률상위, 4:직전대비하락률상위 | 같음 |
FID_COND_SCR_DIV_CODE | 조건화면분류코드 (ex. 11173) | 화면 분류 코드(11173 입력) |
FID_BLNG_CLS_CODE | 소속구분코드 (ex. 0:전체, 1:종가범위연장) | 소속 구분 코드 (값 목록 없음) |
0001을 '거래소'와 '코스피'로 다르게 적었지만 가리키는 시장은 같습니다(유가증권시장). 공매도 순위(FHPST04820000)에 있던 코스닥150 3003은 이 API 목록에는 없습니다. 같은 저장소의 examples_llm 판 함수 시그니처 주석은 FID_BLNG_CLS_CODE를 "소속구분코드"로만 적고, 값 목록은 독스트링에만 있습니다.
3. 요청·응답 실물
Postman 샘플의 원본 요청 줄입니다(저장소 원문 그대로).
{{PROD}}/uapi/domestic-stock/v1/quotations/exp-closing-price?fid_cond_mrkt_div_code=J&fid_cond_scr_div_code=11173&fid_input_iscd=0001&fid_blng_cls_code=0&fid_rank_sort_cls_code=0
응답 필드는 chk_exp_closing_price.py의 COLUMN_MAPPING이 전부입니다.
COLUMN_MAPPING = {
'stck_shrn_iscd': '주식 단축 종목코드',
'hts_kor_isnm': 'HTS 한글 종목명',
'stck_prpr': '주식 현재가',
'prdy_vrss': '전일 대비',
'prdy_vrss_sign': '전일 대비 부호',
'prdy_ctrt': '전일 대비율',
'sdpr_vrss_prpr': '기준가 대비 현재가',
'sdpr_vrss_prpr_rate': '기준가 대비 현재가 비율',
'cntg_vol': '체결 거래량'
}
NUMERIC_COLUMNS = ['전일 대비율', '기준가 대비 현재가 비율']
공식 함수(ka._url_fetch)를 거치지 않고 requests로 직접 부르면 이렇게 됩니다. 앱키·토큰은 환경변수에서 읽고 코드에 쓰지 않습니다(API 키 보안).
import os, requests, pandas as pd
from datetime import datetime
from zoneinfo import ZoneInfo
URL = "https://openapi.koreainvestment.com:9443/uapi/domestic-stock/v1/quotations/exp-closing-price"
headers = {
"content-type": "application/json; charset=utf-8",
"authorization": f"Bearer {os.environ['KIS_TOKEN']}",
"appkey": os.environ["KIS_APPKEY"],
"appsecret": os.environ["KIS_APPSECRET"],
"tr_id": "FHKST117300C0",
"custtype": "P",
}
def exp_closing(iscd="0000", sort="2"): # 2 = 하한가마감예상
params = {
"FID_COND_MRKT_DIV_CODE": "J",
"FID_INPUT_ISCD": iscd,
"FID_RANK_SORT_CLS_CODE": sort,
"FID_COND_SCR_DIV_CODE": "11173", # 고정
"FID_BLNG_CLS_CODE": "0",
}
r = requests.get(URL, headers=headers, params=params, timeout=5)
body = r.json()
if body.get("rt_cd") != "0":
raise RuntimeError(f"{body.get('msg_cd')} {body.get('msg1')}")
df = pd.DataFrame(body.get("output") or [])
df["fetched_at"] = datetime.now(ZoneInfo("Asia/Seoul")) # 응답에 시각 필드가 없다
return df
rt_cd가 "0"이 아닐 때의 msg_cd 해석은 KIS API 에러코드를, 장 막판에 종목을 여럿 조회하다 EGW00201(초당 거래건수 초과)이 나면 EGW00201 해결을 보십시오.
4. 함정 5가지
① '예상체결가' 필드가 없다 — stck_prpr는 "주식 현재가"
API 이름은 "장마감 예상체결가"인데 응답 9필드 어디에도 antc_cnpr(예상 체결가)가 없습니다. 가격 필드는 stck_prpr 하나이고 공식 매핑은 '주식 현재가'입니다. 종가 단일가 구간에 이 값이 예상체결가를 담는지, 직전 체결가를 담는지는 저장소 어디에도 적혀 있지 않습니다. 봇이 이 값으로 가격 판단을 한다면 같은 시각에 같은 종목을 FHKST01010200으로 불러 output2.antc_cnpr와 대조하는 검증을 한 번은 돌려야 합니다.
def cross_check(code, df):
r = requests.get(
"https://openapi.koreainvestment.com:9443/uapi/domestic-stock/v1/quotations/inquire-asking-price-exp-ccn",
headers={**headers, "tr_id": "FHKST01010200"},
params={"FID_COND_MRKT_DIV_CODE": "J", "FID_INPUT_ISCD": code}, timeout=5).json()
antc = r["output2"]["antc_cnpr"]
prpr = df.loc[df["stck_shrn_iscd"] == code, "stck_prpr"].iloc[0]
return {"code": code, "exp_closing.stck_prpr": prpr, "orderbook.antc_cnpr": antc}
② 연속조회를 하지 않는다 — 받은 게 전부라는 보장이 없다
같은 저장소의 순위 API(FHPST04820000 등)는 응답 헤더 tr_cont가 M/F면 N으로 다시 부르는 재귀(max_depth 기본 10)가 들어 있습니다. exp_closing_price()는 ka._url_fetch(API_URL, tr_id, "", params) 한 번으로 끝납니다. 몇 행까지 주는지 명세에 없으므로, 응답 헤더의 tr_cont를 직접 로그로 남겨 뒤에 더 있는지 확인하십시오. 연속조회 처리 패턴은 KIS 잔고 연속조회와 같습니다.
③ 정렬 기준 '직전'이 응답에 없다
FID_RANK_SORT_CLS_CODE의 3(직전대비상승률상위)·4(직전대비하락률상위)는 '직전' 가격 대비 등락률로 줄을 세우는데, 응답에는 prdy_*(전일 대비)와 sdpr_*(기준가 대비)만 있고 직전 가격·직전 대비율 필드가 없습니다. 받은 표를 봇이 같은 기준으로 다시 정렬하거나 검증할 방법이 없다는 뜻입니다. 정렬 순서에 의존하는 로직(예: "상위 5개만")을 쓴다면 prdy_ctrt처럼 응답에 있는 열로 직접 다시 정렬하고, 그 기준을 명세서에 적어 두는 편이 재현 가능합니다.
④ 전일 대비와 기준가 대비가 다른 날이 있다
응답에 prdy_vrss(전일 대비)와 sdpr_vrss_prpr(기준가 대비 현재가)가 따로 있습니다. 평소에는 기준가가 전일 종가와 같지만, 권리락·배당락·액면분할처럼 기준가가 조정되는 날에는 둘이 갈라집니다. 상한가·하한가 판정은 가격제한폭이 걸리는 기준가 쪽으로 해야 하는데, prdy_ctrt만 보고 "+29%니 상한가 근접"이라고 판단하면 기준가 조정일에 틀립니다. 두 열이 다르면 로그에 경고를 남기는 한 줄을 넣어 두십시오. 기준가·상하한가 필드는 현재가 API FHKST01010100에서 종목별로 확인할 수 있습니다.
⑤ 시장코드는 J뿐, 시간대 해석은 직접 해야 한다
허브의 FHKST01010200은 FID_COND_MRKT_DIV_CODE에 J(KRX)·NX(NXT)·UN(통합)을 받지만, 이 API의 공식 설명은 J:주식 하나입니다. 종가 단일가는 KRX 정규장 막판(통상 15:20~15:30) 구간이고, 응답에는 조회 시각도 장운영 구분 코드도 없습니다. 그래서 위 코드처럼 fetched_at을 직접 붙여야 나중에 "15:22에 본 목록"인지 "15:31에 본 목록"인지 구분됩니다. 종가 단일가 밖에서 이 API가 무엇을 돌려주는지는 저장소에 적혀 있지 않습니다. FID_BLNG_CLS_CODE=1(종가범위연장)도 의미 설명이 독스트링 한 줄뿐이라, 쓰려면 HTS [0183] 화면과 대조해 확인한 뒤 쓰십시오. KRX·NXT 시간대 차이는 NXT·KRX 주문 라우팅에 정리했습니다.
5. 봇에서 쓰는 법 — 종가 주문 직전 점검
이 API는 "무엇을 살까"가 아니라 "이미 정한 종가 주문을 그대로 내도 되나"를 점검하는 데 맞습니다. 예를 들어 종가에 매수하는 리밸런싱·종가 진입 전략이라면, 주문 직전에 하한가마감예상(sort="2") 목록을 받아 대상 종목이 있으면 주문을 보류하고 사람에게 알립니다.
targets = {"005930", "000660"} # 오늘 종가 매수 예정 종목
low = exp_closing(iscd="0000", sort="2") # 하한가마감예상
hit = targets & set(low["stck_shrn_iscd"])
for code in hit:
row = low[low["stck_shrn_iscd"] == code].iloc[0]
if row["prdy_vrss"] != row["sdpr_vrss_prpr"]: # ④ 기준가 조정일 경고
log.warning("%s 기준가≠전일종가", code)
notify(f"{code} 하한가 마감 예상 — 종가 매수 보류 ({row['fetched_at']:%H:%M:%S})")
orders = [c for c in targets if c not in hit]
알림은 텔레그램 봇 모니터링 구조를 그대로 쓰면 됩니다. 장 막판에는 주문·취소·잔고 조회가 몰리므로 이 조회는 1회로 끝내고, 종목별 확인이 필요할 때만 ①의 cross_check를 붙입니다.
장마감 예상 목록은 그 순간의 예상일 뿐이며 실제 종가와 다를 수 있습니다. 상한가·하한가 마감 예상 종목이 이후 어떻게 움직일지는 이 API가 알려 주지 않으므로 매매 신호가 아니라 점검용 필터로 쓰십시오. 종가 단일가 시간·가격제한폭 등 시장 제도는 한국거래소 공지를, API 명세는 KIS Developers 공식 문서를 확인하십시오(2026-10-10 공식 저장소 기준).
6. 자주 묻는 질문
KIS 장마감 예상체결가 API의 tr_id와 URL은 무엇인가요?
tr_id는 FHKST117300C0이고 GET /uapi/domestic-stock/v1/quotations/exp-closing-price 로 호출합니다. FID_COND_MRKT_DIV_CODE=J, FID_COND_SCR_DIV_CODE=11173(고정), FID_INPUT_ISCD(0000 전체·0001 코스피·1001 코스닥·2001 코스피200·4001 KRX100), FID_RANK_SORT_CLS_CODE(0~4), FID_BLNG_CLS_CODE 다섯 개가 모두 필수입니다. 공식 저장소 기준 모의투자 제공 칸은 비어 있습니다.
응답의 stck_prpr가 예상체결가인가요?
공식 COLUMN_MAPPING은 stck_prpr를 '주식 현재가'로만 적고 있고, 응답에 antc_cnpr(예상 체결가) 필드는 없습니다. 종목별 예상체결가가 필요하면 주식현재가 호가/예상체결(FHKST01010200)의 output2.antc_cnpr를 쓰고, 두 값을 같은 시각에 한 번 대조해 보는 것이 안전합니다.
상한가마감예상 목록이 전부 다 오나요?
보장되지 않습니다. 공식 함수는 tr_cont를 빈 값으로 고정해 한 번만 호출하고 연속조회 재귀가 없습니다. 응답 헤더의 tr_cont 값을 로그로 남겨 뒤에 데이터가 더 있는지 확인하고, 필요하면 N으로 다시 호출하는 처리를 직접 넣어야 합니다.
하한가 마감 예상 종목은 다음 날 반등하나요?
그렇게 단정할 수 없습니다. 이 API는 종가 단일가 구간의 예상 상황을 보여 줄 뿐 이후 가격 방향을 알려 주지 않습니다. 자동매매에서는 매수·매도 신호보다 종가 주문 직전에 보류하거나 사람에게 알리는 점검 필터로 쓰는 것이 일반적입니다.