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

키움 ka10037 외국계창구 — trde_tp 1이 거꾸로다

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

ka10037(외국계창구매매상위요청)의 trde_tp는 1:순매수, 2:순매도, 3:매수, 4:매도이고, 바로 옆 ka10034(외인기간별매매상위)는 1:순매도, 2:순매수, 3:순매매입니다 — 같은 "1"을 넣으면 정반대 목록이 옵니다. 키움 명세 337개 API 전체를 세어 보니 trde_tp 1의 뜻이 "순매수"(5개)·"순매도"(2개)·"0이 순매수"(4개) 세 갈래로 갈립니다. 또 ka10037은 외국인 투자자가 아니라 외국계 증권사 창구 기준이고, sort_tp 1(금액)으로 정렬되는 netprps_prica(순매수대금)는 백만원 단위입니다. (공식 명세 kiwoom_api_spec.json·예제, 2026-10-18 main 953e5db 기준)

목차

  1. ka10034와 무엇이 다른가 — 창구 vs 투자자
  2. 요청·응답 실물
  3. trde_tp 1의 뜻이 세 갈래다 — 명세 전수
  4. 함정 5가지
  5. 최소 호출 코드
  6. 자주 묻는 질문

1. ka10034와 무엇이 다른가 — 창구 vs 투자자

ka10034 외인기간별매매상위는 "외국인(투자자 유형)이 기간 동안 몇 주를 샀나"를 줍니다. ka10037은 같은 /api/dostk/rkinfo 경로, 같은 dt 코드표를 쓰지만 집계 기준이 다릅니다. 주문을 받은 증권사가 외국계 회원사인가로 묶은 숫자입니다.

둘이 다른 개념이라는 건 키움 명세가 스스로 보여 줍니다. ka10063(장중투자자별매매요청)의 요청 필드에는 투자자 구분 invsr(6:외국인, 7:기관계, …)이 있고, 그와 별도로 frgn_all(외국계전체, 1:체크, 0:미체크)이 있습니다. 외국인 투자자가 국내 증권사로 주문하거나 국내 투자자가 외국계 창구를 쓰면 두 숫자는 엇갈립니다. 거래원 데이터의 성격은 ka10040 거래원 글에서 자세히 다뤘습니다.

항목ka10037 외국계창구매매상위ka10034 외인기간별매매상위
집계 기준외국계 증권사 창구외국인 투자자
요청 bodymrkt_tp·dt·trde_tp·sort_tp·stex_tp (5개)mrkt_tp·trde_tp·dt·stex_tp (4개)
trde_tp1:순매수, 2:순매도, 3:매수, 4:매도1:순매도, 2:순매수, 3:순매매
정렬 선택sort_tp 1:금액, 2:수량없음 (수량)
목록 키frgn_wicket_trde_upperfor_dt_trde_upper
금액 필드netprps_prica·trde_prica (백만원)없음 — netprps_qty(주)만

ka10034 글에서 "금액 기준이 필요하면 직접 근사해야 한다"고 적었는데, ka10037은 sort_tp 1로 서버가 금액 순으로 줄을 세워 줍니다. 대신 대상이 외국계 창구로 좁아집니다.

2. 요청·응답 실물

명세의 request_example 원문입니다. 공식 예제 examples/국내주식/순위정보/get_domestic_foreign_broker_trade_top.py의 __main__도 같은 값입니다.

POST https://api.kiwoom.com/api/dostk/rkinfo
api-id: ka10037
authorization: Bearer {접근토큰}

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

응답 response_example 원문(3행 중 1행)입니다. 모든 값이 문자열이고, 매도거래량에는 -, 매수거래량에는 + 부호가 붙어 옵니다.

{
    "frgn_wicket_trde_upper": [
        {
            "rank": "1",
            "stk_cd": "005930",
            "stk_nm": "삼성전자",
            "cur_prc": "69",
            "pred_pre_sig": "3",
            "pred_pre": "0",
            "flu_rt": "0.00",
            "sel_trde_qty": "-0",
            "buy_trde_qty": "+0",
            "netprps_trde_qty": "0",
            "netprps_prica": "0",
            "trde_qty": "0",
            "trde_prica": "0"
        }
    ],
    "return_code": 0,
    "return_msg": "정상적으로 처리되었습니다"
}
element한글명명세 설명
sel_trde_qty · buy_trde_qty매도·매수거래량단위: 1주, 부호가 포함된 숫자
netprps_trde_qty순매수거래량단위: 1주, 부호가 포함된 숫자
netprps_prica순매수대금단위: 백만원
trde_qty · trde_prica거래량 · 거래대금1주 · 백만원
flu_rt등락율단위: %, 부호 포함 소수점 둘째 자리
pred_pre_sig전일대비기호1:상한가, 2:상승, 3:보합, 4:하한가, 5:하락

3. trde_tp 1의 뜻이 세 갈래다 — 명세 전수

"ka10034만 특이한 것 아닌가" 싶어서 kiwoom_api_spec.json의 apis 337개에서 요청 필드 trde_tp를 가진 TR을 전부 뽑아 첫 번째 코드의 뜻으로 묶었습니다(주문 TR의 호가구분 trde_tp는 제외).

for name, api in spec["apis"].items():
    for f in api["request"]["body"]:
        if f["element"] == "trde_tp":
            print(name, api["meta"]["URL"], f["description"])
trde_tp 체계TR (경로)
1 = 순매수, 2 = 순매도ka10037 · ka10039 · ka10062 · ka10065 (rkinfo) · ka30002 (elw)
1 = 순매도, 2 = 순매수ka10034 (rkinfo) · ka10044 (mrkcond) · ka10058 (stkinfo, "순매도:1, 순매수:2") · ka10035는 1 = 연속순매도
0 = 순매수, 1 = 매수, 2 = 매도ka10059 · ka10060 · ka10064 · ka10066

같은 rkinfo 경로 안에서도 ka10034·ka10035는 1이 매도 쪽, ka10037·ka10039·ka10062·ka10065는 1이 매수 쪽입니다. 여러 순위 TR을 함수 하나로 감싸며 trde_tp="1"을 "순매수"로 하드코딩하면, 에러 없이 절반의 TR에서 반대 목록을 받습니다. 순위 TR을 묶어 쓰는 구조는 rkinfo 순위조회 글을 참고하십시오.

trde_tp "1" ka10037·ka10039·ka10062·ka10065 → 순매수 ka10034·ka10044·ka10058 → 순매도 ka10059·ka10060·ka10064·ka10066 → 매수 (0이 순매수)
같은 값, 세 가지 뜻 — kiwoom_api_spec.json 전수 기준

4. 함정 5가지

① 상수는 TR별로 둔다

BUY = "1" 같은 전역 상수는 위 표 때문에 성립하지 않습니다. TRDE_TP = {"ka10037": {"net_buy": "1"}, "ka10034": {"net_buy": "2"}}처럼 TR 코드를 키로 두고, 첫 운영 호출에서 상위 종목의 netprps_trde_qty 부호가 의도와 맞는지 한 번 확인하는 단언문을 넣어 두면 안전합니다.

② 매도거래량은 -가 붙어 온다

예시의 sel_trde_qty는 "-0", buy_trde_qty는 "+0"입니다. 부호는 방향 표시일 가능성이 높으므로 순매수를 직접 다시 계산할 때 buy - sel로 쓰면 두 수를 더한 값이 나옵니다. 서버가 주는 netprps_trde_qty를 쓰거나, 직접 계산하려면 abs()를 먼저 씌우십시오. 예시 값이 전부 0이라 부호 규칙은 운영 응답으로 한 번 확인해야 합니다.

③ 금액 단위가 TR마다 다르다 — 백만원 vs 천원

ka10037의 netprps_prica·trde_prica는 백만원입니다. 한 회원사가 많이 사고판 종목을 주는 ka10039(증권사별매매상위)의 netprps_amt·buy_amt·sell_amt는 천원입니다. "외국계 전체 vs 특정 외국계 회원사" 비중을 계산하면 1,000배가 어긋나니, 저장할 때 원 단위로 통일하십시오. ka10039는 mmcm_cd(회원사코드)가 필수라 ka10102(회원사 리스트)를 먼저 불러야 합니다.

④ 공식 예시는 자리표시자다

response_example 3행은 전부 005930 삼성전자이고 현재가가 69·316·675, 거래량·대금은 모두 0입니다. 형식 확인용이라 이 값으로 단위 테스트의 기대값을 만들면 안 됩니다. 운영 응답에서 같은 종목코드가 두 번 나오면 결측·중복으로 처리하는 방어 코드를 두십시오.

⑤ 공식 예제는 숫자로 바꿔 주지 않는다

예제의 NUMERIC_COLUMNS는 거래대금·거래량·등락율·매도거래량·매수거래량·순매수거래량·순매수대금·현재가 여덟 개이고 전일대비(pred_pre)는 빠져 있습니다. 게다가 이 목록은 화면 출력용 천 단위 쉼표(_format_display)에만 쓰입니다. 예시 응답을 pandas 2.2.3 DataFrame으로 만들어 보면 13개 열이 전부 object입니다. 그대로 netprps_prica로 정렬하면 문자열 순서("9" > "10000")가 나옵니다. dt 설명의 10;10일 오타와 기준일 필드 부재도 ka10034와 같습니다(ka10034 글 ②).

명세가 비워 둔 것 — 어떤 회원사가 "외국계"로 분류되는지(ka10102 응답의 gb 필드 의미는 명세에 설명이 없고 예시 10개는 모두 "0"), stex_tp 2(NXT)·3(통합)에서 숫자가 어떻게 달라지는지, 한 번에 몇 종목이 오는지는 명세에 없습니다. 첫 운영 호출에서 확인이 필요합니다.

5. 최소 호출 코드

토큰은 환경변수에서 읽고, 연속조회는 공식 예제와 같은 MAX_PAGES = 10·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"]
TRDE   = {"net_buy": "1", "net_sell": "2", "buy": "3", "sell": "4"}   # ka10037 전용 ①
DT_OK  = {"0", "1", "5", "10", "20", "60"}
NUM    = ["cur_prc", "pred_pre", "flu_rt", "sel_trde_qty", "buy_trde_qty",
          "netprps_trde_qty", "netprps_prica", "trde_qty", "trde_prica"]

def ka10037(side="net_buy", dt="0", sort_tp="1", mrkt_tp="000", stex_tp="1",
            max_pages=10, delay=0.2):
    if str(dt) not in DT_OK:
        raise ValueError(f"ka10037 dt={dt} 불가")
    body = {"mrkt_tp": mrkt_tp, "dt": str(dt), "trde_tp": TRDE[side],
            "sort_tp": sort_tp, "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": "ka10037"}
        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"ka10037 {j.get('return_code')}: {j.get('return_msg')}")
        rows += j.get("frgn_wicket_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_trde_qty"] = df["sel_trde_qty"].abs()                   # ②
    df["netprps_won"] = df["netprps_prica"] * 1_000_000             # ③ 원 단위
    df = df.drop_duplicates("stk_cd")                               # ④
    df["dt"], df["asof"] = dt, pd.Timestamp.now().strftime("%Y-%m-%d")
    return df

return_code가 0이 아닐 때의 의미는 키움 return_code 글, cont-yn·next-key는 연속조회 글에 있습니다. 순위 TR 여러 개를 연달아 부를 때는 키움 429 호출 제한을 먼저 계산하십시오.

봇에서 쓴다면 장 마감 뒤 같은 dt로 ka10037(외국계 창구)과 ka10034(외국인 투자자)를 함께 받아 asof와 저장하고, 두 목록에 같이 나오는 종목을 표시하는 정도가 무난합니다. 한국투자증권 계좌라면 종목별 투자자 수급은 FHKST01010900(주식현재가 투자자)으로 받습니다. 외국계 창구 수급은 해석이 갈리는 데이터라 이 목록 자체가 매매 근거가 되지는 않으며, 이 글은 특정 종목이나 전략을 권하지 않습니다. 명세는 바뀔 수 있으니 키움 REST API 공식 문서로 현재 값을 확인하십시오.

6. 자주 묻는 질문

ka10037에서 trde_tp 1을 넣으면 순매수 상위가 나오나요?

네. ka10037의 trde_tp는 1:순매수, 2:순매도, 3:매수, 4:매도입니다. 반면 같은 rkinfo 경로의 ka10034(외인기간별매매상위)는 1:순매도, 2:순매수, 3:순매매라서 같은 값 1이 반대 목록을 돌려줍니다. TR마다 코드표를 따로 상수로 두십시오.

외국계 창구 순매수와 외국인 순매수는 같은 숫자인가요?

다른 집계입니다. 외국계 창구는 주문을 접수한 외국계 증권사(회원사) 기준이고, 외국인 순매수는 투자자 유형 기준입니다. 키움 명세도 둘을 따로 둡니다 — ka10063(장중투자자별매매) 요청에 투자자 구분 invsr 6:외국인과 별도로 frgn_all(외국계전체) 체크 필드가 있습니다. 외국인 전체 수급이 필요하면 ka10034나 ka10059를 쓰십시오.

ka10037의 netprps_prica는 원 단위인가요?

아닙니다. 명세상 순매수대금 netprps_prica와 거래대금 trde_prica는 단위가 백만원입니다. 한 회원사 기준인 ka10039(증권사별매매상위)의 netprps_amt는 천원 단위라서 두 TR 금액을 섞으면 1,000배가 어긋납니다.

어느 외국계 증권사가 샀는지도 ka10037로 알 수 있나요?

ka10037 응답에는 회원사 필드가 없고 외국계 창구 합계만 종목별로 옵니다. 특정 회원사를 보려면 ka10102로 회원사코드를 받은 뒤 ka10039(증권사별매매상위)에 mmcm_cd로 넣거나, 종목 단위로 ka10040(당일주요거래원)의 상위 5개 거래원을 보십시오.

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

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

무료 상담 시작하기