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

키움 ka10051 업종별투자자순매수 — 지수가 100배다

키움 REST · 시세 2026-11-04 · 약 6분 읽기 · 알고랩 AlgoLab
한 줄 요약

ka10051(업종별투자자순매수요청)은 POST /api/dostk/sect에 시장(mrkt_tp 0·1)과 금액/수량 구분을 보내면 업종마다 개인·외국인·기관계 등 13개 칸의 순매수를 목록(inds_netprps)으로 주는 TR입니다. 함정은 숫자 형식입니다. 공식 예시의 cur_prc "+265381"은 명세엔 '단위: 원'이지만 계산해 보면 지수 2653.81의 100배 정수이고, flu_rt "352"도 3.52%입니다. inds_cd는 001_AL로 와서 ka20001의 001과 그냥 합쳐지지 않습니다. 공식 예제 함수는 base_dt 기본값이 20241107로 박혀 있습니다. (키움 명세 kiwoom_api_spec.json·공식 예제, 저장소 main 953e5db 기준, pandas 2.2.3 실측, 2026-11-04 확인. 실계좌 응답이 아니라 명세 예시 기준입니다)

목차

  1. 언제 ka10051을 쓰나
  2. 요청·응답 실물
  3. 예시대로 짜면 깨지는 곳 5가지 (실측)
  4. 검산 — 어떤 식이 맞고 어떤 식이 안 맞나
  5. 받아서 정규화하는 코드
  6. 자주 묻는 질문

1. 언제 ka10051을 쓰나

"오늘 코스피에서 외국인이 어느 업종을 샀나"처럼 업종 단위 수급을 한 번에 보는 TR입니다. 키움 REST의 비슷한 TR과 요청을 명세 원문으로 대조하면 이렇게 갈립니다.

TRURL단위 대상금액/수량 코드금액 단위
ka10051 업종별투자자순매수sect업종 전체 목록0 금액 · 1 수량억원 (수량은 1000주)
ka10059 종목별투자자기관별stkinfo종목 하나, 일자별1 금액 · 2 수량백만원
ka10063 장중투자자별매매mrkcond투자자 하나, 종목 목록1 금액&수량백만원
ka20001 업종현재가sect업종 하나의 지수없음거래대금 백만원

업종 지수 자체가 필요하면 ka20001·ka20006이고, ka10051의 현재가 칸은 곁다리로 보는 편이 안전합니다. 이유는 3절에 있습니다.

2. 요청·응답 실물

필드한글명필수값 (명세 원문)
mrkt_tp시장구분Y0 코스피 · 1 코스닥
amt_qty_tp금액수량구분Y0 금액 · 1 수량
base_dt기준일자NYYYYMMDD
stex_tp거래소구분Y1 KRX · 2 NXT · 3 통합

헤더에는 api-id: ka10051과 authorization: Bearer …를 넣습니다. ka10063의 mrkt_tp는 000·001·101이지만 업종 TR은 한 자리입니다. 명세의 응답 예시 첫 행입니다.

{
    "inds_cd": "001_AL",
    "inds_nm": "종합(KOSPI)",
    "cur_prc": "+265381",
    "pre_smbol": "2",
    "pred_pre": "+9030",
    "flu_rt": "352",
    "trde_qty": "1164",
    "sc_netprps": "+255",
    "insrnc_netprps": "+0",
    "invtrt_netprps": "+0",
    "bank_netprps": "+0",
    "jnsinkm_netprps": "+0",
    "endw_netprps": "+0",
    "etc_corp_netprps": "+0",
    "ind_netprps": "-0",
    "frgnr_netprps": "-622",
    "native_trmt_frgnr_netprps": "+4",
    "natn_netprps": "+0",
    "samo_fund_netprps": "+1",
    "orgn_netprps": "+601"
}

순매수 칸 13개의 단위는 모두 "억원 혹은 1000주, 부호가 포함된 숫자"입니다. 칸 이름이 ka10059와 달라서(sc_netprps·jnsinkm_netprps·endw_netprps 대 fnnc_invt·etc_fnnc·penfnd_etc) 같은 주체인지는 명세만으로 확정할 수 없습니다. 한글명은 각각 증권·종신금·기금, 금융투자·기타금융·연기금등입니다.

3. 예시대로 짜면 깨지는 곳 5가지 (실측)

명세의 응답 예시를 Python 3.10.4·pandas 2.2.3에서 돌리고, 공식 예제 함수를 가짜 클라이언트로 호출해 실제로 나가는 요청을 찍었습니다.

① cur_prc는 '원'이 아니라 지수의 100배다

명세 설명은 cur_prc·pred_pre 모두 "단위: 원"입니다. 그런데 전일대비와 등락률을 맞춰 보면 100배 정수로 읽어야 앞뒤가 맞습니다.

001_AL 종합(KOSPI) 지수 2653.81  전일대비 90.30   계산 등락률 3.5225%  flu_rt/100 3.52
002_AL 대형주      지수 2659.64  전일대비 106.90  계산 등락률 4.1877%  flu_rt/100 4.19
naive 시장 필터: 265381 > 2500 -> True

같은 /api/dostk/sect 경로의 ka20001은 "-2394.49"처럼 소수로 주고 설명도 "단위: 지수"입니다. 두 TR의 지수를 같은 변수로 받아 이평선과 비교하면 조건이 항상 참이 됩니다. ka20006의 100배 정수 함정과 같은 유형으로, 업종지수 글에 시장 필터 관점으로 정리돼 있습니다.

② flu_rt는 부호도 소수점도 없다

명세는 "부호 포함 소수점 둘째 자리까지 포맷된 백분율"이라 적지만 예시는 "352"·"419"입니다. 100으로 나눠야 하고, 예시가 전부 상승 행이라 하락일에 -가 붙는지는 확인되지 않습니다. 방향은 pre_smbol(1 상한가, 2 상승, 3 보합, 4 하한가, 5 하락)이나 부호가 있는 pred_pre로 보는 편이 안전합니다. 같은 뜻의 칸 이름도 TR마다 다릅니다. ka10051 pre_smbol, ka20001 pred_pre_sig, ka10059 pre_sig입니다.

③ inds_cd에 _AL이 붙어 합쳐지지 않는다

stex_tp=3(통합) 예시의 업종코드는 001_AL·002_AL입니다. ka20001 요청과 ka10101 업종코드 목록은 001 형식이라 그대로 merge하면 아무것도 붙지 않습니다.

'001' == '001_AL' -> False
'001_AL'.split('_')[0] -> '001'

stex_tp 1·2일 때 접미사가 어떻게 오는지는 명세에 없습니다. 조회 조건을 저장 키에 함께 넣고, 첫 응답에서 직접 확인하십시오.

④ 1을 보내면 금액이 아니라 수량이다

대부분의 투자자 TR은 1이 금액, 2가 수량인데 ka10051은 0이 금액, 1이 수량입니다. 공통 함수에 amt_qty_tp="1"을 박아 두면 오류 없이 수량이 돌아오고, 억원으로 믿고 쓰게 됩니다. 단위도 억원이라 종목 TR의 백만원과 100배 차이입니다. 합치기 전에 원·주로 환산하십시오. 또 trde_qty 설명은 "단위: 1주"인데 코스피 전체 예시값이 1164이고 ka20001 거래량은 "단위: 1000주"입니다. 실제 값이 어느 쪽인지는 첫 응답을 HTS 수치와 대조해 보셔야 합니다.

⑤ 공식 예제는 날짜를 안 넘기면 2024-11-07을 조회한다

공식 예제 examples/국내주식/업종/get_domestic_sector_investor_net_buy.py의 시그니처는 base_dt: str | None = '20241107'입니다. 명세상 선택 항목이라 빼도 될 것 같지만, 예제 함수를 그대로 가져다 쓰면 과거 날짜가 나갑니다. 오류가 없어서 알아채기 어렵습니다.

get_domestic_sector_investor_net_buy('0', '0', '3')
  -> {'mrkt_tp': '0', 'amt_qty_tp': '0', 'stex_tp': '3', 'base_dt': '20241107'}
get_domestic_sector_investor_net_buy('0', '0', '3', base_dt=None)
  -> {'mrkt_tp': '0', 'amt_qty_tp': '0', 'stex_tp': '3'}

항상 base_dt를 오늘 날짜로 명시하십시오. base_dt를 뺐을 때 서버가 어떤 날짜로 처리하는지는 명세에 적혀 있지 않습니다. 예제의 표시 함수도 계산에는 쓸 수 없습니다. _format_display_value('+265381')은 원문 그대로, NUMERIC_COLUMNS 6개에는 외국인순매수·개인순매수·기관계순매수가 없습니다.

     업종코드      현재가    전일대비  등락율 개인순매수 외국인순매수
0  001_AL  +265381   +9030  352    -0   -622
1  002_AL  +265964  +10690  419   +16   -622

4. 검산 — 어떤 식이 맞고 어떤 식이 안 맞나

예시로 합계를 맞춰 보면 ka10061처럼 깔끔하게 떨어지지 않습니다.

기관계 orgn vs 세부 8칸 합(증권·보험·투신·은행·종신금·기금·국가·사모)
  001_AL  601 vs 256  차이 345
  002_AL  602 vs 256  차이 346
개인 + 외국인 + 기관계 + 기타법인 + 내국인대우외국인
  001_AL  -17
  002_AL    0
ind_netprps "-0" : int -> 0, float -> -0.0, pd.to_numeric -> 0
001_AL 예시 · 단위 억원 orgn_netprps 601 세부 8칸 256 설명되지 않는 345 세부 칸이 기관계의 전부가 아닐 수도, 예시가 견본일 수도 있다 — 실응답으로 확인
명세 예시 기준. 예시값은 형식 견본이라 실제 응답에서 다시 재야 한다

예시는 형식 견본일 가능성이 있어 여기서 결론을 내리면 안 됩니다. 그러니 "세부 합 = 기관계"를 코드의 전제로 두지 마십시오. 필요한 값은 orgn_netprps를 직접 읽고, 합계 차이는 오류로 멈추지 말고 로그로 남겨 실제 응답으로 판단합니다. 칸이 억원 단위로 반올림돼 온다면 작은 차이는 원래 생기므로 ±1억은 허용합니다. "-0"은 숫자로는 0이지만, float은 -0.0을 만들어 그대로 출력하면 표에 -0.0이 찍힙니다. 부호만 세는 코드라면 x < 0으로 판정하십시오(-0.0 < 0은 False).

5. 받아서 정규화하는 코드

import os, time, requests, pandas as pd
from datetime import datetime, timedelta, timezone

URL = "https://api.kiwoom.com/api/dostk/sect"      # 모의: https://mockapi.kiwoom.com
NET = ["sc", "insrnc", "invtrt", "bank", "jnsinkm", "endw", "etc_corp", "ind",
       "frgnr", "native_trmt_frgnr", "natn", "samo_fund", "orgn"]
NET = [f"{c}_netprps" for c in NET]

def num(s):
    """'+265381' -> 265381.0, '-622' -> -622.0, '-0' -> 0.0, '' -> NaN"""
    s = str(s).strip().replace(",", "")
    if s in ("", "None", "nan"):
        return float("nan")
    v = float(s.lstrip("+-") or "nan")
    return -v if s.startswith("-") and v else v

def ka10051(token, mrkt_tp="0", amt=True, stex_tp="1", base_dt=None, max_pages=10):
    today = datetime.now(timezone(timedelta(hours=9))).strftime("%Y%m%d")
    body = {"mrkt_tp": mrkt_tp, "amt_qty_tp": "0" if amt else "1",   # 0이 금액이다
            "base_dt": base_dt or today, "stex_tp": stex_tp}
    rows, cont, key = [], None, None
    for _ in range(max_pages):
        h = {"api-id": "ka10051", "authorization": f"Bearer {token}",
             "Content-Type": "application/json;charset=UTF-8"}
        if cont == "Y":
            h.update({"cont-yn": "Y", "next-key": key})
        r = requests.post(URL, headers=h, json=body, timeout=10)
        j = r.json()
        if j.get("return_code") != 0:
            raise RuntimeError(f"ka10051 {j.get('return_code')} {j.get('return_msg')}")
        rows += j.get("inds_netprps", [])
        cont, key = r.headers.get("cont-yn"), r.headers.get("next-key")
        if cont != "Y":
            break
        time.sleep(0.2)                                # 공식 예제와 같은 간격
    df = pd.DataFrame(rows)
    df["inds_code"] = df.inds_cd.str.split("_").str[0]  # ka20001·ka10101 과 합칠 키
    for c in NET + ["pred_pre", "cur_prc", "flu_rt"]:
        df[c] = df[c].map(num)
    df["index"] = df.cur_prc / 100                      # 명세 '원'이지만 지수 × 100
    df["pct"] = df.flu_rt / 100
    if amt:
        df[NET] = df[NET] * 100_000_000                 # 억원 -> 원
    df.insert(0, "base_dt", body["base_dt"])
    return df

# token = os.environ["KIWOOM_TOKEN"]      # 키·토큰은 코드에 적지 말 것
# ka10051(token).nlargest(5, "frgnr_netprps")[["inds_nm", "frgnr_netprps"]]

코드는 예시 응답으로 변환까지 확인했고, 실계좌 호출은 하지 않았습니다. 첫 실행에서 index가 HTS 지수와 같은지 꼭 대조하십시오. 토큰과 return_code 처리는 키움 REST API 자동매매 가이드, 연속조회는 next-key 글에 있습니다. 업종 수급을 매매 조건으로 쓰려면 후행 지표라는 점을 감안해야 하며, 조합 방법은 수급 필터 글을 참고하세요. 수급 데이터는 방향을 예측해 주지 않고, 과거 흐름이 앞으로 반복된다는 보장도 없습니다.

6. 자주 묻는 질문

ka10051과 ka10059는 어떻게 다른가요?

ka10051은 POST /api/dostk/sect로 코스피나 코스닥의 업종 목록을 받고 순매수 금액 단위가 억원입니다. ka10059는 /api/dostk/stkinfo로 종목 하나를 일자별로 받고 금액 단위가 백만원입니다. amt_qty_tp도 ka10051은 0이 금액, ka10059는 1이 금액입니다.

ka10051의 cur_prc는 지수로 바로 쓸 수 있나요?

아닙니다. 공식 예시의 +265381은 전일대비와 등락률로 검산하면 지수 2653.81의 100배 정수입니다. 명세 설명은 '단위: 원'이라 실제 응답을 HTS 지수와 먼저 대조한 뒤 100으로 나누어 쓰십시오. 지수가 목적이면 ka20001이나 ka20006을 쓰는 편이 낫습니다.

base_dt를 빼면 오늘 데이터가 오나요?

명세상 선택 항목이지만 뺐을 때 어떤 날짜로 처리하는지는 적혀 있지 않습니다. 공식 예제 함수는 기본값이 20241107이라 인자를 생략하면 그 날짜를 조회합니다. 항상 날짜를 명시하십시오.

업종코드를 ka20001과 맞추려면 어떻게 하나요?

stex_tp 3 예시의 inds_cd는 001_AL 형식입니다. 밑줄 앞부분을 잘라 001로 만든 키로 ka20001이나 ka10101 업종코드와 합치면 됩니다. stex_tp 1, 2의 접미사는 명세에 없으니 첫 응답에서 확인하십시오.

업종 수급을 보는 봇, 만들어 드립니다

ka10051·ka20001로 업종 수급과 지수를 받아 단위를 맞춰 저장하고 매매 조건으로 쓰는 키움 REST 봇을 맞춤 제작합니다. 24시간 빠른 답변 가능합니다.

무료 상담 시작하기