KIS API 매수가능조회 — ORD_DVSN 00 아닌 01
inquire-psbl-order, tr_id = TTTC8908R)로
가능수량을 물을 때는 ORD_DVSN을 01(시장가)로 넣어야 합니다.
한국투자증권 공식 예제 설명에 따르면
00(지정가)으로 조회하면 종목증거금율이 반영되지 않아
실제보다 큰 수량이 돌아옵니다. 그 수량 그대로 주문하면 거부되거나 미수가 납니다.
금액은 미수를 쓰지 않으면 nrcvb_buy_amt(미수없는매수금액),
쓰면 max_buy_amt(최대매수금액)를 보고,
수량은 각각 nrcvb_buy_qty·max_buy_qty입니다.
봇이라면 nrcvb_* 쪽이 기본값입니다.
KIS API 발급 가이드대로 토큰까지 받고 첫 주문을 넣는 단계에서, 대부분 "얼마나 살 수 있는지"를 예수금으로 계산합니다. 그리고 며칠 뒤 주문이 거부되거나, 사려던 것보다 많이 사졌거나, 미수가 잡힌 것을 발견합니다.
이 글은 매수가능조회 API 하나만 다룹니다. 에러코드 목록에는 안 나오는 문제인데, 응답이 정상으로 오면서 값만 틀리기 때문입니다.
이 글의 순서
- 왜 예수금으로 계산하면 틀리나
- 요청 — 경로·헤더·파라미터 7개
ORD_DVSN함정 — 00과 01이 다른 값을 준다- 응답 필드 — 어떤 걸 봐야 하나
- 봇에 붙이는 실제 코드
- 호출 제한과 다종목 처리 · 체크리스트 · FAQ
1. 왜 예수금으로 계산하면 틀리나
계좌에 1,000만 원이 있다고 1,000만 원어치를 다 살 수 있는 것이 아닙니다. 실제 주문 가능 금액은 여러 값이 합쳐진 결과입니다.
- 종목증거금율 — 종목마다 다릅니다. 증거금율이 높은 종목은 같은 예수금으로 살 수 있는 수량이 줄어듭니다.
- 미결제 주문 — 이미 걸어 둔 매수 주문이 금액을 잡아먹고 있습니다.
- 재사용가능금액(
ruse_psbl_amt) — 매도 대금 중 다시 쓸 수 있는 부분. - 대용금(
ord_psbl_sbst) · CMA평가금액(cma_evlu_amt) — 포함 여부를 요청에서 정합니다.
이걸 봇에서 직접 계산하려면 증거금 체계를 그대로 재구현해야 합니다. 증권사가 이미 계산해 주는 API가 있으니, 주문 직전에 한 번 물어보는 편이 단순하고 정확합니다.
잔고 조회와는 다른 API입니다. 잔고 조회는 지금 무엇을 들고 있는가를 알려 주고, 매수가능조회는 지금 무엇을 얼마나 더 살 수 있는가를 알려 줍니다. 둘을 섞어 쓰다가 예수금 필드로 수량을 계산하는 것이 이 문제의 출발점입니다.
2. 요청 — 경로·헤더·파라미터 7개
| 항목 | 값 |
|---|---|
| 경로 | /uapi/domestic-stock/v1/trading/inquire-psbl-order |
| 메서드 | GET |
tr_id (실전) | TTTC8908R |
tr_id (모의) | VTTC8908R |
custtype | P (개인) |
| 1회 조회 | 최대 1건 (종목 하나) |
실전과 모의는 tr_id 앞 글자가 T/V로 갈립니다.
도메인·앱키까지 세트로 바뀌므로 전환 시 빠뜨리기 쉽습니다 —
모의투자 실전 전환에 목록을 정리해 두었습니다.
| 파라미터 | 의미 | 값 예시 |
|---|---|---|
CANO | 종합계좌번호 (앞 8자리) | "12345678" |
ACNT_PRDT_CD | 계좌상품코드 (뒤 2자리) | "01" |
PDNO | 종목번호 6자리 | "005930" |
ORD_UNPR | 주문단가 (1주당 가격) | "55000" |
ORD_DVSN | 주문구분 | "01" (시장가) |
CMA_EVLU_AMT_ICLD_YN | CMA평가금액 포함 | "N" |
OVRS_ICLD_YN | 해외 포함 | "N" |
계좌를 CANO·ACNT_PRDT_CD 둘로 나눠 넣는 규칙은 KIS API 공통입니다.
"01"이 정수 1로 읽혀 실패하는 사고가 잦으니
계좌 파라미터 처리를 함께 보십시오.
3. ORD_DVSN 함정 — 00과 01이 다른 값을 준다
이 글에서 가장 중요한 항목입니다.
ORD_DVSN은 "어떤 주문 방식으로 살 건지"를 알려 주는 값인데,
이 값에 따라 계산 방식 자체가 달라집니다.
공식 설명 그대로: 특정 종목 전량매수 시 가능수량을 확인할 경우
ORD_DVSN:00(지정가)은 종목증거금율이 반영되지 않습니다.
따라서 반드시 ORD_DVSN:01(시장가)로 지정하여
종목증거금율이 반영된 가능수량을 확인해야 합니다.
예외 하나. IOC처럼 특정 주문구분으로 실제 주문을 낼 계획이라면,
조회할 때도 같은 주문구분을 넣어 그 조건에서의 가능수량을 확인해야 합니다.
원칙은 "조회의 ORD_DVSN을 실제 주문의 ORD_DVSN과 맞춘다"이고,
단순히 최대 수량만 알고 싶을 때의 기준값이 01입니다.
주문구분 값 전체는 주문 유형 정리를 참고하십시오.
4. 응답 필드 — 어떤 걸 봐야 하나
응답 output에는 열 개가 넘는 필드가 들어 있습니다.
실제로 봇이 쓰는 것은 네 개이고, 나머지는 그 값들이 어떻게 나왔는지를 보여 주는 내역입니다.
| 필드 | 의미 | 언제 쓰나 |
|---|---|---|
nrcvb_buy_amt | 미수없는매수금액 | 미수 안 씀 · 금액 기준 |
nrcvb_buy_qty | 미수없는매수수량 | 미수 안 씀 · 수량 기준 |
max_buy_amt | 최대매수금액 | 미수 사용 · 금액 기준 |
max_buy_qty | 최대매수수량 | 미수 사용 · 수량 기준 |
ord_psbl_cash | 주문가능현금 | 내역 확인용 |
ord_psbl_sbst | 주문가능대용 | 내역 확인용 |
ruse_psbl_amt | 재사용가능금액 | 내역 확인용 |
psbl_qty_calc_unpr | 가능수량계산단가 | 어떤 단가로 계산됐는지 |
cma_evlu_amt | CMA평가금액 | 요청에서 포함했을 때 |
봇의 기본값은 nrcvb_*입니다.
미수는 결제일까지 대금을 채우지 못하면 반대매매로 이어지는데,
무인으로 도는 봇은 그 상황을 스스로 수습하지 못합니다.
사람이 지켜보지 않는 코드에 max_buy_qty를 쓰지 마십시오.
psbl_qty_calc_unpr(가능수량계산단가)는 검증용으로 유용합니다.
내가 보낸 ORD_UNPR과 다른 단가로 계산됐다면,
수량이 예상과 어긋나는 이유가 거기 있습니다.
5. 봇에 붙이는 실제 코드
import requests
BASE = "https://openapi.koreainvestment.com:9443"
PATH = "/uapi/domestic-stock/v1/trading/inquire-psbl-order"
def buy_power(access_token, cano, acnt_prdt_cd, pdno, price,
use_margin=False, ord_dvsn="01"):
"""매수가능조회 — 기본은 미수 없는 값(nrcvb_*)을 돌려준다"""
headers = {
"content-type": "application/json; charset=utf-8",
"authorization": f"Bearer {access_token}",
"appkey": APP_KEY,
"appsecret": APP_SECRET,
"tr_id": "TTTC8908R", # 모의계좌는 VTTC8908R
"custtype": "P",
}
params = {
"CANO": cano, # 문자열 8자리
"ACNT_PRDT_CD": acnt_prdt_cd, # 문자열 2자리
"PDNO": pdno,
"ORD_UNPR": str(int(price)),
"ORD_DVSN": ord_dvsn, # ← 01 (증거금율 반영)
"CMA_EVLU_AMT_ICLD_YN": "N",
"OVRS_ICLD_YN": "N",
}
d = requests.get(BASE + PATH, headers=headers, params=params, timeout=5).json()
if d.get("rt_cd") != "0":
raise RuntimeError(f"KIS 에러 [{d.get('msg_cd')}] {d.get('msg1','').strip()}")
o = d["output"]
amt_key, qty_key = ("max_buy_amt", "max_buy_qty") if use_margin \
else ("nrcvb_buy_amt", "nrcvb_buy_qty")
return {
"amount": int(o[amt_key]),
"quantity": int(o[qty_key]),
"calc_unpr": int(o["psbl_qty_calc_unpr"]),
}
주문 직전 호출은 이렇게 됩니다. 가능수량을 그대로 쓰지 말고 전략이 원하는 수량과 비교해 작은 쪽을 택하십시오.
want = 100 # 전략이 계산한 수량
bp = buy_power(token, CANO, ACNT, "005930", price=71300)
qty = min(want, bp["quantity"])
if qty <= 0:
log.warning("매수 여력 없음 — amount=%s qty=%s", bp["amount"], bp["quantity"])
return # 주문을 아예 내지 않는다
if qty < want:
log.info("수량 축소 %s → %s (여력 %s원)", want, qty, bp["amount"])
place_order(pdno="005930", qty=qty, price=71300)
여력이 0일 때 주문을 내지 마십시오.
수량 0으로 주문 요청을 보내거나 거부될 걸 알면서 넣는 코드는
실패 응답을 유량으로 소모합니다. 여러 종목을 도는 루프라면
이게 쌓여 EGW00201 초당 거래건수 초과로 이어집니다.
여력 확인·수량 계산·체결 처리까지 도는 상태로 만들어 드립니다. 24시간 빠른 답변 가능합니다.
무료 상담 시작하기 →6. 호출 제한과 다종목 처리
이 API는 한 번에 한 종목입니다. 공식 예제 설명에도 실전·모의 모두 한 번의 호출에 최대 1건이라고 적혀 있습니다. 즉 10종목을 매수하는 전략이면 주문 10번 + 여력 조회 10번이 됩니다.
- 종목마다 다시 부르십시오. 증거금율은 종목별로 다르므로 한 종목의 결과를 다른 종목에 적용하면 틀립니다.
- 같은 종목을 짧은 시간에 반복 호출하지 마십시오. 체결이 없었다면 값이 거의 그대로입니다. 짧은 캐시로 충분합니다.
- 여력 조회도 주문과 같은 유량 풀을 씁니다. 조회 호출 수를 계산에 넣지 않으면 장 초반에 한도가 터집니다 (호출 제한 설계).
체크리스트
ORD_DVSN을01로 넣었는가 (또는 실제 주문구분과 일치시켰는가)- 미수를 쓰지 않는다면
nrcvb_buy_qty를 보고 있는가 CANO·ACNT_PRDT_CD가 문자열인가- 실전/모의에 맞는
tr_id(TTTC8908R/VTTC8908R)를 썼는가 rt_cd를 검사하는가 (HTTP 200만 보고 넘어가지 않는가)- 전략 수량과 가능수량 중 작은 쪽을 택하는가
- 여력이 0일 때 주문을 내지 않는 분기가 있는가
- 종목마다 다시 조회하는가
자주 묻는 질문
Q. ORD_DVSN에 무엇을 넣어야 하나요?
가능수량 확인이 목적이면 01(시장가)입니다.
00(지정가)은 종목증거금율이 반영되지 않아 실제보다 큰 수량이 나옵니다.
IOC 등 특정 주문구분으로 주문할 계획이면 조회에도 같은 값을 쓰십시오.
Q. nrcvb_buy_amt와 max_buy_amt는 무엇이 다른가요?
미수 사용 여부입니다. nrcvb_buy_amt는 미수 없이 가능한 금액,
max_buy_amt는 미수를 썼을 때의 최대 금액입니다.
무인 봇이라면 nrcvb_*가 기본값입니다.
Q. 여러 종목을 한 번에 조회할 수 있나요?
없습니다. 한 번의 호출에 최대 1건이라 PDNO에 종목 하나를 넣습니다.
종목 수만큼 호출이 늘어나므로 유량 제한을 함께 고려하십시오.
Q. 예수금만 보고 주문하면 안 되나요?
권장하지 않습니다. 실제 가능 금액에는 종목증거금율·미결제 주문·재사용가능금액이 함께 반영됩니다. 증권사가 계산해 주는 값을 주문 직전에 한 번 받는 편이 정확합니다.
확인 캐치. 이 글의 경로·tr_id·파라미터명·응답 필드명과
ORD_DVSN 관련 주의사항은 2026년 8월 14일 기준
한국투자증권이 공개한 오픈API 예제 코드의 명세와 설명에 근거합니다.
증거금율 체계·미수 정책·API 스펙은 증권사와 종목에 따라 다르고 변경될 수 있으므로
구현 전 KIS Developers 공식 문서와 증권사 안내에서 확인하십시오.
본 글은 특정 종목이나 수익률에 대한 어떠한 예측이나 권유도 담고 있지 않습니다.
마무리
정리하면 한 줄입니다.
가능수량은 ORD_DVSN을 01로 물어보고, 미수 없는 값(nrcvb_*)을 쓴다.
00으로 받은 수량은 증거금율이 빠진 숫자라, 그대로 주문에 넣으면 언젠가 반드시 사고가 납니다.