AlgoLab Blog · 키움 계좌 실무 · 2026

키움 REST API 주문가능금액 — kt00001 vs kt00010

키움증권 · 계좌·주문 2026-08-24 · 약 8분 읽기 · 알고랩 AlgoLab
한 줄 요약 계좌 전체로 얼마까지 주문할 수 있는지는 kt00001(예수금상세현황요청)의 ord_alow_amt이고, 이 종목을 이 가격에 몇 주 살 수 있는지는 kt00010(주문인출가능금액요청)입니다. 둘 다 POST /api/dostk/acnt로 같고 헤더 api-id로만 갈립니다. 사람을 가장 많이 잡는 지점은 증거금률입니다 — 같은 계좌라도 종목에 따라 살 수 있는 금액이 달라서, kt0000120stk_ord_alow_amt부터 100stk_ord_alow_amt까지 증거금률별로 금액을 따로 주고, kt00010종목코드·매수가격을 받아 profa_40ord_alowq 같은 주문가능수량까지 계산해 돌려줍니다. 예수금 entr을 주가로 나눠 수량을 정하면 증거금률 높은 종목에서 주문이 거부됩니다.

키움 REST API로 주문을 내는 코드를 붙이고 나면 바로 다음 질문이 옵니다. “그래서 몇 주를 넣지?” 많은 첫 봇이 여기서 이렇게 씁니다.

qty = int(예수금 / 현재가)     # ← 이 한 줄이 문제입니다

이 식은 모의투자에서는 잘 돌아가고 실전에서 조용히 깨집니다. 운이 좋으면 주문이 거부돼 바로 알게 되고, 운이 나쁘면 미수가 잡혀 며칠 뒤에야 알게 됩니다. 키움에서 “살 수 있는 돈”은 계좌당 한 개가 아니라 종목의 증거금률만큼 여러 개이기 때문입니다. 이 글은 그 값을 받아오는 kt00001·kt00010 두 TR만 다룹니다.

목차

  1. 증거금률 — “예수금 ÷ 주가”가 틀리는 이유
  2. kt00001 예수금상세현황요청 — 계좌 단위 금액
  3. 함정 ① 예수금 4형제 — entr · ord_alow_amt · pymn_alow_amt · d2_entra
  4. 함정 ② qry_tp 3과 2는 다른 답을 준다
  5. 함정 ③ 필드명이 숫자로 시작한다
  6. kt00010 주문인출가능금액요청 — 종목 단위 수량
  7. 함정 ④ uv(매수가격)가 필수다 — 시장가 봇은 뭘 넣나
  8. 실전 코드 — 매수 수량 결정기
  9. KIS는 어떻게 다른가
  10. 자주 묻는 질문

1. 증거금률 — “예수금 ÷ 주가”가 틀리는 이유

종목마다 증거금률이 정해져 있습니다. 증거금률 40% 종목이면 매수 대금의 40%만 있어도 주문이 들어가고, 증거금률 100% 종목은 현금이 전액 있어야 합니다. 그래서 “이 계좌로 얼마까지 살 수 있나”에는 답이 하나가 아닙니다. kt00001 응답이 정확히 그 구조로 되어 있습니다.

응답 항목한글명봇에서의 의미
ord_alow_amt주문가능금액계좌 단위 기준 금액
20stk_ord_alow_amt20%종목주문가능금액증거금률 20% 종목이면 여기까지
30stk_ord_alow_amt · 40stk_ord_alow_amt · 50stk_ord_alow_amt · 60stk_ord_alow_amt30·40·50·60% 종목주문가능금액증거금률이 오를수록 작아진다
100stk_ord_alow_amt100%종목주문가능금액전액 현금 종목 — 가장 작다

여기서 나오는 사고ord_alow_amt 하나만 읽어 수량을 계산하면 증거금률이 높게 지정된 종목에서 한도를 넘겨 주문이 거부됩니다. 더 나쁜 경우는 일부만 체결되고 나머지가 미수로 남는 것입니다. 증거금률은 종목별로 바뀌므로 봇이 매일 같은 값을 가정하면 안 됩니다.

계좌 단위 (kt00001) ord_alow_amt · 주문가능금액 20stk_ord_alow_amt 40stk_ord_alow_amt 100stk_... 증거금률이 높을수록 살 수 있는 금액은 작아진다 종목 단위 (kt00010) 입력 stk_cd · trde_tp=2 · uv 내려감 profa_40ord_alowq = 주문가능수량 금액이 아니라 몇 주로 돌려준다 = 봇이 그대로 쓰기 좋은 형태 종목·가격이 정해졌으면 이쪽이 정답
kt00001은 계좌 기준 금액, kt00010은 종목·가격 기준 수량

2. kt00001 예수금상세현황요청 — 계좌 단위 금액

먼저 계좌 전체 그림을 보는 TR입니다. 공식 명세 기준 요청 형태는 다음과 같습니다.

구분
MethodPOST
URL/api/dostk/acnt
헤더 api-idkt00001 (필수)
헤더 authorizationBearer {접근토큰} (필수) · cont-yn/next-key는 연속조회용(선택)
Body qry_tp조회구분 — 3:추정조회, 2:일반조회 (필수)

실제 호출은 이렇게 생겼습니다.

import requests

def deposit_detail(base_url, token, qry_tp="3"):
    """kt00001 예수금상세현황요청"""
    res = requests.post(
        f"{base_url}/api/dostk/acnt",
        headers={
            "Content-Type": "application/json;charset=UTF-8",
            "authorization": f"Bearer {token}",
            "api-id": "kt00001",
        },
        json={"qry_tp": qry_tp},
        timeout=5,
    )
    res.raise_for_status()
    return res.json()

응답에서 주문 여력과 직접 관련된 항목만 추리면 이런 모양입니다.

{
  "entr":                "000000012500000",   // 예수금
  "ord_alow_amt":        "000000012500000",   // 주문가능금액
  "pymn_alow_amt":       "000000009800000",   // 출금가능금액
  "20stk_ord_alow_amt":  "000000062500000",   // 20% 종목 기준
  "40stk_ord_alow_amt":  "000000031250000",   // 40% 종목 기준
  "100stk_ord_alow_amt": "000000012500000",   // 100% 종목 기준
  "d2_entra":            "000000012500000",   // d+2 추정예수금
  "ch_uncla":            "000000000000000",   // 현금미수금
  "crd_grnt_rt":         "000000000000.00"    // 신용담보비율(%)
}

※ 위 금액은 구조를 보이기 위한 예시입니다. 항목명·타입·자릿수 표기는 공식 명세 기준이며, 실제 값은 계좌 상태에 따라 달라집니다.

3. 함정 ① 예수금 4형제 — entr · ord_alow_amt · pymn_alow_amt · d2_entra

kt00001의 응답에는 금액 항목이 아주 많습니다. 그중 봇이 헷갈리기 쉬운 넷을 먼저 갈라 두는 것이 좋습니다.

항목한글명봇이 쓰는 곳
entr예수금계좌에 들어 있는 현금 — 주문 한도와 같지 않다
ord_alow_amt주문가능금액매수 여력 판단은 여기서 출발
pymn_alow_amt출금가능금액계좌 밖으로 뺄 수 있는 돈 — 주문과 무관
d1_entra / d2_entrad+1 / d+2 추정예수금결제 반영 후 남을 현금 추정

국내 주식은 체결 즉시 결제가 끝나지 않습니다. 그래서 매도한 돈이 예수금에는 잡혀도 출금가능금액에는 아직 안 잡히는 구간이 생깁니다. kt00001d1_slby_exct_amt(d+1매도매수정산금)· d2_buy_exct_amt(d+2매수정산금)·d1_pymn_alow_amt(d+1출금가능금액)을 잔뜩 주는 이유가 그것입니다.

실무 규칙 — “살 수 있나”는 ord_alow_amt 계열로, “며칠 뒤 현금이 남나”는 d1_entra·d2_entra로 나눠 쓰십시오. pymn_alow_amt(출금가능금액)를 매수 여력으로 쓰면 실제보다 작게 잡혀 봇이 아무것도 안 사는 조용한 버그가 됩니다.

미수가 걸려 있는지는 ch_uncla(현금미수금)·ch_uncla_tot(현금미수금합계)로 확인합니다. 이 값이 0이 아니면 봇은 신규 매수를 멈추고 알림부터 보내는 것이 맞습니다.

4. 함정 ② qry_tp 3과 2는 다른 답을 준다

kt00001의 요청 Body는 qry_tp 하나뿐인데, 이 하나가 필수이고 값에 따라 결과가 달라집니다. 공식 명세의 설명은 3:추정조회, 2:일반조회이고, 키움이 배포하는 공식 Postman 컬렉션의 예시 요청도 qry_tp3으로 보냅니다.

# 같은 계좌인데 응답 금액이 다를 수 있다
snap_est  = deposit_detail(BASE, token, qry_tp="3")   # 추정조회
snap_norm = deposit_detail(BASE, token, qry_tp="2")   # 일반조회

print(int(snap_est["ord_alow_amt"]), int(snap_norm["ord_alow_amt"]))

다만 봇 코드에서는 둘 중 하나를 상수로 고정하고, 로그에 함께 남기십시오. “금액이 안 맞는다”는 상황이 왔을 때 어떤 조회구분으로 찍힌 값인지 모르면 원인을 찾을 수 없습니다.

5. 함정 ③ 필드명이 숫자로 시작한다

명세를 읽을 때는 안 보이다가 코드를 짜는 순간 튀어나오는 문제입니다. 20stk_ord_alow_amt·30stk_ord_alow_amt·100stk_ord_alow_amt전부 숫자로 시작합니다. 파이썬 식별자는 숫자로 시작할 수 없으므로 다음이 전부 막힙니다.

# 전부 SyntaxError 또는 사용 불가
value = resp.20stk_ord_alow_amt              # SyntaxError

@dataclass
class Deposit:
    20stk_ord_alow_amt: str                  # SyntaxError

Deposit = namedtuple("Deposit", ["20stk_ord_alow_amt"])   # ValueError

딕셔너리 키로 접근하는 것만 됩니다. 그래서 증거금률별 금액은 이름을 하드코딩하지 말고 규칙으로 만들어 두는 편이 낫습니다.

MARGIN_RATES = (20, 30, 40, 50, 60, 100)

def orderable_by_margin(snap: dict) -> dict:
    """kt00001 응답에서 증거금률별 주문가능금액만 뽑는다."""
    out = {}
    for r in MARGIN_RATES:
        key = f"{r}stk_ord_alow_amt"          # 20stk_ord_alow_amt ...
        raw = snap.get(key)
        if raw is None:
            continue
        out[r] = int(raw)                     # 0-padding·부호 포함 String을 int로
    return out

타입도 함께 처리하십시오. kt00001의 금액 항목은 명세상 전부 String이고 설명이 “단위: 원, 좌측 0-padding 처리된 부호 포함 15자리 숫자”입니다 (kt00010은 같은 성격의 12자리). 앞의 0lstrip("0")으로 떼면 값이 0일 때 빈 문자열이 되므로 int()를 쓰십시오. crd_grnt_rt(신용담보비율)만 % 단위 백분율이라 float()이 필요합니다.

6. kt00010 주문인출가능금액요청 — 종목 단위 수량

kt00001이 “계좌에 얼마”라면 kt00010“이 종목을 이 가격에 몇 주”를 직접 계산해 줍니다. 봇이 실제로 필요한 형태가 이쪽입니다.

요청 항목한글명필수설명
stk_cd종목번호Y종목 코드
trde_tp매매구분Y1:매도, 2:매수
uv매수가격Y단위: 원
trde_qty매매수량N단위: 1주
io_amt입출금액N단위: 원
exp_buy_unp예상매수단가N단위: 원

응답은 증거금률별로 금액과 수량이 쌍으로 옵니다.

응답 항목한글명
profa_20ord_alow_amt / profa_20ord_alowq증거금20% 주문가능금액 / 주문가능수량
profa_30ord_alow_amt · profa_40ord_alow_amt · profa_50ord_alow_amt · profa_60ord_alow_amt · profa_100ord_alow_amt30·40·50·60·100% 주문가능금액 (수량은 ...alowq)
profa_rdex_60ord_alow_amt / profa_rdex_60ord_alowq증거금감면60% 주문가능금 / 수량
profa_rdex_aplc_tp증거금감면적용구분 — 0:일반, 1:60%감면
ord_alowa주문가능현금
ord_pos_repl주문가능대용
wthd_alowa / nxdy_wthd_alowa인출가능금액 / 익일인출가능금액
uncla미수금
cmsn / pur_exct_amt수수료 / 매입정산금
d2entraD2 추정예수금

주목할 것 두 가지.profa_40ord_alowq금액이 아니라 수량(1주 단위)이라 봇이 그대로 주문 수량에 넣을 수 있습니다. ② cmsn(수수료)·pur_exct_amt(매입정산금)가 함께 와서 수수료까지 포함한 정산 금액을 같은 응답에서 봅니다 — kt00001에는 없는 정보입니다.

한편 kt0000120stk_ord_alow_amt인데 kt00010profa_20ord_alow_amt입니다. 같은 개념인데 이름 규칙이 다릅니다. 두 TR의 파서를 같은 함수로 재사용하면 여기서 조용히 None이 나옵니다.

7. 함정 ④ uv(매수가격)가 필수다 — 시장가 봇은 뭘 넣나

uv(매수가격)가 필수라서 kt00010가격을 넣지 않으면 물어볼 수 없는 구조입니다. 지정가 봇은 그대로 넣으면 되지만 시장가로 사는 봇은 애매해집니다. 실무에서 쓰는 방법은 대체로 이렇습니다.

호가로 주문가를 정하는 방법은 호가 조회 — 10호가·잔량으로 지정가 정하기에 정리돼 있습니다 (KIS 기준이지만 원리는 같습니다).

8. 실전 코드 — 매수 수량 결정기

지금까지의 함정을 전부 반영한 형태입니다. 규칙은 둘 — “증거금률을 모르면 가장 보수적인 100% 기준을 쓴다”, “API가 준 수량을 그대로 쓰지 않는다”.

import math, requests

MARGIN_RATES = (20, 30, 40, 50, 60, 100)
SAFETY = 0.95          # API 수량을 그대로 쓰지 않는다

def order_available(base_url, token, stk_cd, price, trde_tp="2"):
    """kt00010 주문인출가능금액요청"""
    res = requests.post(
        f"{base_url}/api/dostk/acnt",
        headers={
            "Content-Type": "application/json;charset=UTF-8",
            "authorization": f"Bearer {token}",
            "api-id": "kt00010",
        },
        json={
            "stk_cd": stk_cd,
            "trde_tp": trde_tp,      # 1:매도, 2:매수
            "uv": str(int(price)),   # 필수 — 가격 없이는 못 묻는다
            "trde_qty": "",
            "io_amt": "",
            "exp_buy_unp": "",
        },
        timeout=5,
    )
    res.raise_for_status()
    return res.json()


def buy_quantity(base_url, token, stk_cd, price, margin_rate=None):
    """살 수 있는 수량을 보수적으로 계산한다."""
    r = order_available(base_url, token, stk_cd, price)

    # 미수가 잡혀 있으면 신규 매수 금지
    if int(r.get("uncla", "0") or 0) > 0:
        return 0, "미수금 존재 — 신규 매수 중단"

    # 증거금률을 모르면 가장 보수적인 100% 기준을 쓴다
    rate = margin_rate if margin_rate in MARGIN_RATES else 100
    qty_key = f"profa_{rate}ord_alowq"          # profa_40ord_alowq ...
    raw = r.get(qty_key)
    if raw is None:
        return 0, f"{qty_key} 없음 — 응답 스펙 확인 필요"

    qty = int(raw)                               # 0-padding String을 int로
    qty = math.floor(qty * SAFETY)               # 조회~주문 사이 가격 변동 흡수
    return max(qty, 0), f"{rate}% 기준 {qty}주"

kt00001과 함께 쓰면 계좌 상태 점검 → 종목별 수량 결정의 두 단이 됩니다.

def can_trade_today(base_url, token):
    """장 시작 전 계좌 게이트 — kt00001"""
    snap = deposit_detail(base_url, token, qry_tp="3")

    if int(snap.get("ch_uncla_tot", "0") or 0) > 0:
        return False, "현금미수금합계가 0이 아님"
    if int(snap.get("int_npay_amt_tot", "0") or 0) > 0:
        return False, "신용이자미납합계가 0이 아님"

    budget = int(snap["ord_alow_amt"])
    if budget < 100_000:
        return False, f"주문가능금액 부족 {budget:,}원"

    return True, f"주문가능금액 {budget:,}원"

호출 횟수를 조심하십시오. 종목 100개를 스캔하며 종목마다 kt00010을 부르면 키움 REST API의 요청 제한에 걸립니다 (키움 REST API 429 — 허용된 요청 개수 초과). 후보를 먼저 좁히고, 실제로 주문할 종목에만 부르는 순서가 맞습니다.

9. KIS는 어떻게 다른가

한국투자증권 KIS API의 매수가능조회도 주문가능금액과 최대 주문가능수량을 함께 주는 구조라 키움 kt00010과 성격이 비슷합니다. 다만 함정이 다릅니다 — KIS는 ORD_DVSN 값을 잘못 넣어 수량이 어긋나는 사고가 흔합니다 (KIS API 매수가능조회 — ORD_DVSN 00 아닌 01).

키움 REST API 전체 그림은 키움 REST API 자동매매 완전 가이드에 있습니다.

“API가 허락하는 최대 수량”과 “전략이 허락하는 수량”은 다른 문제입니다. profa_100ord_alowq가 500주라고 해서 500주를 사야 하는 것이 아닙니다 — 한 번에 얼마를 태울지는 포지션 사이징 4가지에서 정합니다.

10. 자주 묻는 질문

Q. 모의투자에서도 kt00001·kt00010이 되나요?

공식 Postman 컬렉션에는 운영(PRD)과 모의투자(MOCK) 두 벌이 있고 두 TR 모두 양쪽에 존재합니다. 다만 도메인과 접근토큰이 다르므로 운영 토큰으로 모의 도메인을 부르면 인증에서 막힙니다 (토큰 유효기간 — expires_dt와 폐기).

Q. 증거금률을 API로 알 수 있나요?

이 두 TR만으로는 “이 종목의 증거금률이 몇 %인지”가 나오지 않습니다. 그래서 위 코드가 margin_rate를 모르면 100% 기준으로 떨어지도록 만든 것입니다. 율 자체를 물으려면 별도 TR이 필요합니다kt00011 증거금율별주문가능수량조회요청이 stk_profa_rt(종목)·profa_rt(계좌)·aplc_rt(적용) 세 가지를 돌려줍니다 (키움 증거금율 API kt00011 — 증거금율이 3개로 온다). 증거금률은 증권사 정책에 따라 바뀌므로 키움 REST API 공식 가이드와 HTS 표기를 함께 확인하십시오. 보수적으로 잡아 못 사는 것은 복구되지만, 넉넉하게 잡아 미수가 나는 것은 복구가 어렵습니다.

Q. 매도할 때도 kt00010을 쓰나요?

trde_tp1(매도)을 넣을 수는 있습니다. 다만 매도 수량의 상한은 보유 수량이라 실무에서는 잔고 TR kt00018을 봅니다. 이미 걸어 둔 주문 수량은 다시 팔 수 없으므로 미체결도 함께 봐야 합니다 (미체결 조회 — ka10075 함정 5가지).

Q. 응답에 항목이 안 보이는 것이 있습니다.

kt00001·kt00010의 응답 항목은 명세상 대부분 필수가 아닙니다(required = N). 계좌 상태에 따라 빠질 수 있다는 뜻이므로, 대괄호로 직접 인덱싱하지 말고 resp.get(...)에 기본값을 주는 편이 안전합니다. 위 예제 코드가 전부 .get()or 0을 쓰는 이유입니다.

마무리

키움 REST API에서 “얼마나 살 수 있나”는 한 개의 숫자가 아닙니다. 계좌 단위로는 kt00001ord_alow_amt20stk_ord_alow_amt~100stk_ord_alow_amt가 있고, 종목 단위로는 kt00010profa_40ord_alowq 같은 수량을 바로 돌려줍니다. 예수금 entr을 주가로 나누는 한 줄이 위험한 이유는 거기에 증거금률도, 미수도, 결제 주기도 없기 때문입니다.

※ 본문의 요청·응답 항목명과 코드값(kt00001·kt00010)은 키움증권이 배포하는 공식 Postman 컬렉션(운영 PRD) 기준이며 2026-08-24 확인한 내용입니다. 증거금률·결제 주기·수수료 등 계좌 정책은 증권사와 계좌 유형에 따라 다르고 변경될 수 있으므로, 제작 직전에는 키움 REST API 공식 가이드와 계좌 약관을 다시 확인하십시오. 본 글은 수익이나 시장 방향을 예측하지 않으며 투자 판단의 근거가 아닙니다.

주문 여력 계산부터 통째로 맡기시겠어요?

증거금률·미수·연속조회까지 반영한 주문 엔진과 전략 봇을 함께 제작합니다.
24시간 빠른 답변 가능합니다.

제작 상담하기