AlgoLab Blog · 키움증권 REST · 수급 데이터 · 2026

키움 ka10034 외인기간별매매상위 — 기간은 6개뿐이다

키움 REST · 순위정보 2026-10-16 · 약 6분 읽기 · 알고랩 AlgoLab
한 줄 요약

ka10034(외인기간별매매상위요청)는 "최근 N일 외국인 누적 순매수 상위"를 시장 단위로 한 번에 주지만, N은 고를 수 없습니다. 기간 dt는 0(당일)·1(전일)·5·10·20·60일 여섯 값뿐이고 시작일·기준일 필드가 없어 항상 호출한 날 기준입니다 — 과거 날짜로 백테스트할 데이터는 이 TR로 못 만듭니다. 응답 for_dt_trde_upper의 수급 숫자는 netprps_qty(순매수량, 1주) 하나라 금액은 직접 근사해야 하고, 명세의 dt 설명에는 10;10일 오타가 있어 그대로 파싱하면 ValueError가 납니다(ka10036·ka10037도 같음). (공식 명세 kiwoom_api_spec.json·예제, 2026-10-16 main 953e5db 기준)

목차

  1. ka10035·ka10036과 무엇이 다른가
  2. 요청·응답 실물
  3. 함정 6가지
  4. 최소 호출 코드
  5. 봇에서 쓰는 순서
  6. 자주 묻는 질문

1. ka10035·ka10036과 무엇이 다른가

그제 정리한 ka10035 외인연속순매매상위는 "최근 사흘"만 봤습니다. 20일·60일처럼 더 긴 창으로 외국인이 많이 산 종목을 보려면 같은 /api/dostk/rkinfo 경로의 ka10034를 씁니다. 외국인 관련 순위 TR 셋을 나란히 놓으면 이렇습니다.

항목ka10034 외인기간별매매상위ka10035 외인연속순매매상위ka10036 외인한도소진율증가상위
기간dt 0·1·5·10·20·60base_dt_tp 0:당일·1:전일기준dt 0·1·5·10·20·60
방향trde_tp 1:순매도, 2:순매수, 3:순매매1:연속순매도, 2:연속순매수없음
수급 숫자netprps_qty 순매수량 1개dm1~dm3 + tot소진율·소진율증가
더 주는 것sel_bid·buy_bid·trde_qty·gain_pos_stkcntlimit_exh_rtposs_stkcnt·gain_pos_stkcnt
순위 필드rank 있음없음rank 있음
공통mrkt_tp 000:전체·001:코스피·101:코스닥, stex_tp 1:KRX·2:NXT·3:통합 (모두 필수)
호출한 날 (고정) 60거래일 전 dt=60 dt=20 dt=10 5 0·1 3일 · 15일 · "작년 3월" — 넣을 칸이 없음
ka10034의 창은 오른쪽 끝이 항상 오늘이다 (창 길이는 개념도)

2. 요청·응답 실물

요청 body는 네 개가 전부이며 모두 필수입니다. 명세의 request_example 원문이고, 공식 예제 examples/국내주식/순위정보/get_domestic_foreign_investor_period_trade_top.py의 __main__도 같은 값을 씁니다.

{
    "mrkt_tp": "001",
    "trde_tp": "2",
    "dt": "0",
    "stex_tp": "1"
}

헤더는 api-id: ka10034, authorization: Bearer …, 연속조회 시 cont-yn·next-key입니다. 명세의 response_example을 pandas로 펼친 결과입니다(필드 일부, stk_nm은 세 행 모두 삼성전자).

rank stk_cd cur_prc pred_pre_sig pred_pre sel_bid buy_bid netprps_qty
   1 005930  +74800            1   +17200       0  +74800  +290232191
   2 005930 -183500            5     -900 +184900       0  +167189864
   3 005930    4115            3        0       0       0   +59255646
element한글명명세 설명
rank · stk_cd · stk_nm순위 · 종목코드 · 종목명—
cur_prc · pred_pre현재가 · 전일대비단위: 원, 부호가 포함된 숫자
pred_pre_sig전일대비기호1:상한가, 2:상승, 3:보합, 4:하한가, 5:하락
sel_bid · buy_bid매도호가 · 매수호가단위: 원, 부호가 포함된 숫자
trde_qty거래량단위: 1주
netprps_qty순매수량단위: 1주, 부호가 포함된 숫자
gain_pos_stkcnt취득가능주식수단위: 1주

3. 함정 6가지

① 기간은 여섯 값뿐, 날짜는 못 고른다

dt의 허용값은 0·1·5·10·20·60입니다. "3일 순매수 상위"는 없고, ka10131처럼 strt_dt·end_dt를 넣는 길도 없습니다. 더 큰 문제는 기준일이 없다는 것입니다. 2026년 3월 기준 20일 순매수 상위는 그때 저장해 두지 않았으면 다시 받을 수 없습니다. 이 TR로 신호를 만든다면 매일 장 마감 뒤 결과를 파일로 쌓아 두는 것부터 시작해야 하고, 그 전 기간은 ka10059나 ka10008(주식외국인종목별매매동향 — 일자별 chg_qty)로 종목마다 따로 만들어야 합니다.

② 명세 오타 10;10일 — 파서가 죽는다

명세 JSON에서 코드표를 자동으로 뽑는 경우(코드 생성기·MCP 도구 등)에 걸립니다. 실제로 돌린 결과입니다(pandas 2.2.3, Python 3.10).

>>> desc
'0:당일, 1:전일, 5:5일, 10;10일, 20:20일, 60:60일'
>>> dict(p.split(':', 1) for p in desc.split(', '))
ValueError: dictionary update sequence element #3 has length 1; 2 is required
>>> dict(re.split('[:;]', p, maxsplit=1) for p in desc.split(', '))
{'0': '당일', '1': '전일', '5': '5일', '10': '10일', '20': '20일', '60': '60일'}

명세 전체 337개 API 중 이 오타가 있는 요청 필드는 ka10034·ka10036·ka10037 세 개입니다. 사람이 읽을 때는 문제없지만 자동 파싱이면 [:;]로 나누거나 허용값을 상수로 박으십시오.

③ 수량뿐 — 60일이면 금액 근사가 크게 틀린다

숫자는 netprps_qty(1주) 하나입니다. 저가주가 위로 오는 문제는 ka10035와 같아서 cur_prc 절댓값을 곱해 금액으로 바꾸게 되는데, dt가 길수록 그동안 가격이 움직였으므로 오차가 커집니다. 당일·5일은 근사로 쓸 만하고, 20·60일은 순서 비교용으로만 보십시오. 금액이 필요하면 ka10131(amt_qty_tp 0:금액)이 정답입니다. 같은 경로의 ka10037은 sort_tp 1:금액이 있지만 외국계 창구 기준이라 외국인 전체와 다릅니다.

④ 매도호가 0은 "호가 없음"이다

예시 1행은 pred_pre_sig 1(상한가), cur_prc +74,800, pred_pre +17,200입니다. 전일 종가를 역산하면 57,600원이고 상승률은 29.86% — 상한가와 맞습니다. 이 행의 sel_bid는 "0"입니다. 상한가에는 매도 호가가 없으니 0은 가격이 아니라 호가 없음입니다. 스프레드나 지정가를 이 값으로 계산하면 0원 주문이 나갑니다. 0은 결측으로 바꾸고, 주문 가격은 ka10004 호가에서 다시 받으십시오.

⑤ cur_prc의 마이너스는 하락 표시다

예시 2행 cur_prc "-183500"은 pred_pre_sig 5(하락)와 같이 옵니다. 부호는 등락이지 가격이 아니므로 abs()를 거칩니다. 숫자가 전부 부호 붙은 문자열이라는 점도 같아서, pd.to_numeric 없이 정렬하면 순서가 깨집니다(ka10035 글 ⑤에 실행 결과).

⑥ 명세가 비워 둔 것들

4. 최소 호출 코드

토큰은 환경변수에서 읽습니다. 연속조회는 공식 예제와 같이 최대 10페이지(MAX_PAGES = 10), 0.2초 간격(REQUEST_DELAY_SECONDS = 0.2)입니다. 허용값을 상수로 막아 ①·②를 코드에서 끝냅니다.

import os, time, requests
import pandas as pd

BASE   = "https://api.kiwoom.com"            # 모의: https://mockapi.kiwoom.com
TOKEN  = os.environ["KIWOOM_TOKEN"]
DT_OK  = {"0", "1", "5", "10", "20", "60"}   # 명세 허용값 — 그 밖은 호출 전에 거절
NUM    = ["netprps_qty", "trde_qty", "gain_pos_stkcnt", "sel_bid", "buy_bid"]

def ka10034(dt="20", trde_tp="2", mrkt_tp="000", stex_tp="1", max_pages=10, delay=0.2):
    if str(dt) not in DT_OK:
        raise ValueError(f"ka10034 dt={dt} 불가 — {sorted(DT_OK, key=int)}")
    body = {"mrkt_tp": mrkt_tp, "trde_tp": trde_tp, "dt": str(dt), "stex_tp": stex_tp}
    rows, cont_yn, next_key = [], None, None
    for _ in range(max_pages):
        h = {"Content-Type": "application/json;charset=UTF-8",
             "authorization": f"Bearer {TOKEN}", "api-id": "ka10034"}
        if cont_yn == "Y":
            h.update({"cont-yn": "Y", "next-key": next_key})
        r = requests.post(BASE + "/api/dostk/rkinfo", headers=h, json=body, timeout=10)
        j = r.json()
        if j.get("return_code") not in (None, 0):
            raise RuntimeError(f"ka10034 {j.get('return_code')}: {j.get('return_msg')}")
        rows += j.get("for_dt_trde_upper", [])
        cont_yn, next_key = r.headers.get("cont-yn"), r.headers.get("next-key")
        if cont_yn != "Y":
            break
        time.sleep(delay)
    df = pd.DataFrame(rows)
    df[NUM] = df[NUM].apply(pd.to_numeric, errors="coerce")
    df[["sel_bid", "buy_bid"]] = df[["sel_bid", "buy_bid"]].abs().replace(0, pd.NA)  # 0 = 호가 없음
    df["price"] = pd.to_numeric(df["cur_prc"], errors="coerce").abs()
    df["approx_krw"] = df["netprps_qty"] * df["price"]     # dt가 길수록 오차 큼
    df["dt"], df["asof"] = dt, pd.Timestamp.now().strftime("%Y-%m-%d")
    return df

마지막 줄의 asof가 ①의 대책입니다. 매일 이 DataFrame을 날짜별 파일로 저장하면 그날부터 이력이 쌓입니다. 오류 코드는 키움 return_code 글, 연속조회 헤더는 cont-yn·next-key 글에 있습니다.

5. 봇에서 쓰는 순서

  1. 장 마감 뒤 dt 5와 20으로 두 번 부르고 asof를 붙여 저장한다.
  2. 두 목록에 모두 있는 종목, 5일 쪽에만 새로 들어온 종목을 나눈다 — 짧은 창과 긴 창의 겹침을 보는 용도입니다.
  3. "며칠째"가 필요하면 ka10035·ka10131로, 정확한 금액이 필요하면 ka10131로 확인한다.
  4. 한국투자증권 계좌라면 KIS 외국인·기관 순매수 글의 TR을 쓴다.

여러 순위 TR을 연달아 부를 때의 호출 간격은 키움 429 레이트리밋 글을 보십시오. 외국인 수급은 해석이 갈리는 데이터라 이 목록 자체가 매매 근거가 되지는 않습니다. 이 글은 데이터를 정확히 받는 방법을 다루며 특정 종목이나 전략을 권하지 않습니다. 명세는 바뀔 수 있으니 키움 REST API 공식 문서로 현재 값을 확인하십시오.

6. 자주 묻는 질문

ka10034로 3일이나 15일 같은 기간을 조회할 수 있나요?

명세상 안 됩니다. dt에 넣을 수 있는 값은 0(당일)·1(전일)·5·10·20·60일 여섯 개뿐이고 시작일·종료일 필드도 없습니다. 임의 기간이 필요하면 ka10131(dt 0 + strt_dt·end_dt)이나 종목별 일자 데이터(ka10008)를 쓰십시오.

ka10034로 과거 날짜의 외국인 순매수 상위를 받아 백테스트할 수 있나요?

할 수 없습니다. 요청에 기준일이 없어 기간은 항상 호출 시점에서 거꾸로 셉니다. 백테스트용 이력이 필요하면 매일 장 마감 뒤 결과를 직접 저장해 쌓거나, 날짜를 지정할 수 있는 다른 TR로 만들어야 합니다.

ka10034와 ka10035는 무엇이 다른가요?

둘 다 /api/dostk/rkinfo의 외국인 순위 TR입니다. ka10035는 연속 순매매를 D-1·D-2·D-3 수량과 합계로 주고, ka10034는 당일~60일 기간 누적 순매수량 하나(netprps_qty)와 취득가능주식수, 매도·매수호가를 줍니다. ka10034에는 3:순매매 구분이 더 있습니다.

netprps_qty를 금액으로 바꿀 수 있나요?

현재가(cur_prc의 절댓값)를 곱하면 대략적인 금액은 됩니다. 다만 기간이 길수록 그 사이 가격이 움직였으므로 60일 기준에서는 오차가 커집니다. 정확한 금액이 필요하면 금액 구분이 있는 ka10131(amt_qty_tp 0)을 쓰십시오.

외국인 수급 필터를 넣은 키움 봇, 만들어 드립니다

원하는 조건만 말로 알려 주시면 TR 선택과 이력 저장까지 맞춰 드립니다. 24시간 빠른 답변 가능합니다.

무료 상담 시작하기