AlgoLab Blog · 키움 REST API · 거래원/회원사 · 2026

키움 거래원 조회 ka10040 — 외국계는 추정치다

키움 · 거래원 데이터 2026-09-19 · 약 7분 읽기 · 알고랩 AlgoLab
한 줄 요약 키움 거래원 TR은 상위 5개까지만 내려주고, 외국계 수치는 필드명 그대로 추정합입니다. ka10040(당일주요거래원)의 외국계 필드 이름이 frgn_sel_prsm_sum·frgn_buy_prsm_sum매도·매수 추정입니다. 거래원 항목도 sel_trde_ori_1~_5다섯 개가 끝이라 합계를 전체 거래량으로 쓰면 안 됩니다. 그리고 ka10039·ka10043회원사코드 mmcm_cd가 필수ka10102를 먼저 불러야 합니다.

"어느 증권사 창구에서 사고팔았나"를 보는 데이터는 국내 주식에만 있는 층입니다. 키움 REST API도 이걸 TR 아홉 개로 열어 뒀는데, 처음 붙이면 세 군데에서 막힙니다 — 경로가 두 개로 갈려 있고, 필드 이름이 내용과 어긋난 자리가 있으며, 데이터의 성격(추정·상위 5개)을 모르고 쓰면 숫자가 조용히 틀립니다.

이 글은 키움 순위조회 ka10030 계열을 정리한 글에서 거래원 쪽만 떼어낸 것입니다. 순위조회 허브가 "등락률·거래량 랭킹"을 다뤘다면, 여기서는 회원사(증권사) 단위 데이터만 봅니다. 아래 필드명·파라미터는 키움 공식 명세 파일과 examples/ 예제를 2026년 9월 19일 기준으로 읽은 것입니다.

이 글에서 확인할 것

  1. 거래원 TR 아홉 개 지도 — 경로가 두 개다
  2. 상위 5개가 전부다
  3. 외국계는 추정합이다
  4. ka10102를 먼저 불러야 하는 TR
  5. rank_1은 순위가 아니다
  6. 실시간 0F와 이탈 감지

1. 거래원 TR 아홉 개 — 경로가 두 개로 갈린다

같은 "거래원"을 다루는데 /api/dostk/stkinfo(종목정보)/api/dostk/rkinfo(순위정보) 두 경로에 흩어져 있습니다. 메뉴 분류를 그대로 옮긴 결과인데, 코드에서 엔드포인트를 상수로 묶어 두면 절반이 404가 납니다.

TR이름경로무엇을 주나
ka10002주식거래원요청stkinfo종목 1개의 매도·매수 거래원 명 + 수량 5위까지
ka10040당일주요거래원요청rkinfo5위까지 + 거래원코드·증감·외국계 추정합·이탈정보
ka10053당일상위이탈원요청rkinfo상위에서 빠져나간 거래원과 그 시각
ka10038종목별증권사순위요청rkinfo한 종목에 대한 증권사 순위(기간 지정 가능)
ka10042순매수거래원순위요청rkinfo순매수 기준 순위·회원사코드·회원사명만
ka10039증권사별매매상위요청rkinfo증권사가 많이 사고판 종목 목록
ka10037외국계창구매매상위요청rkinfo외국계 창구 기준 종목 순위
ka10043거래원매물대분석요청stkinfo특정 회원사의 일자별 매수·매도·거래비중
ka10102회원사 리스트stkinfo회원사코드 ↔ 이름 매핑 (요청 바디 없음)

방향으로 나누면 이해가 빠릅니다. ka10002·ka10040·ka10038은 "종목 → 증권사"이고, ka10039·ka10043은 "증권사 → 종목"입니다. 입력이 종목코드냐 회원사코드냐가 갈림길입니다.

2. 상위 5개가 전부다

가장 먼저 부딪히는 벽입니다. ka10002 응답은 이렇게 생겼습니다.

{
  "stk_cd": "005930", "stk_nm": "...", "cur_prc": "...",
  "sel_trde_ori_nm_1": "...", "sel_trde_ori_1": "...", "sel_trde_qty_1": "...",
  "buy_trde_ori_nm_1": "...", "buy_trde_ori_1": "...", "buy_trde_qty_1": "...",
  "sel_trde_ori_nm_2": "...", ... ,
  "sel_trde_ori_nm_5": "...", "sel_trde_ori_5": "...", "sel_trde_qty_5": "...",
  "buy_trde_ori_nm_5": "...", "buy_trde_ori_5": "...", "buy_trde_qty_5": "..."
}
// _6 은 존재하지 않는다 — 스펙상 5까지가 끝

⚠ 5개 수량의 합은 그 종목의 전체 거래량이 아닙니다. 6위 이하 거래원의 몫이 통째로 빠져 있습니다. "상위 5개 합 ÷ 전체 거래량"으로 집중도를 계산하는 것은 정의상 100%가 되지 않는 지표이고, 반대로 합계를 전체로 간주해 비중을 나누면 숫자가 부풀려집니다. 거래량 자체가 필요하면 종목정보(ka10001)처럼 전체 거래량을 주는 TR에서 따로 받아 분모로 써야 합니다.

3. 외국계는 추정합이다

ka10040은 5위 목록에 더해 외국계 관련 필드 네 개를 같이 내려줍니다. 이름 자체가 성격을 밝히고 있습니다.

필드한글명
frgn_sel_prsm_sum외국계매도추정
frgn_sel_prsm_sum_chang외국계매도추정합변동
frgn_buy_prsm_sum외국계매수추정
frgn_buy_prsm_sum_chang외국계매수추정합변동

prsm은 추정(推定)입니다. 확정 집계가 아니라는 뜻을 필드명이 스스로 말하고 있습니다. 게다가 거래원은 "주문을 접수한 증권사 창구"일 뿐, 주문을 낸 주체가 누구인지를 확정해 주지 않습니다. 외국계 창구로 들어온 주문이라고 해서 외국인 자금이라고 단정할 수 없고, 그 반대도 마찬가지입니다. 이 데이터로 무엇을 판단할지는 전략의 몫이며, 이 글은 어떤 해석도 권하지 않습니다.

수급 데이터를 투자자 유형(외국인·기관·개인)으로 보고 싶다면 그건 다른 층입니다. 거래원은 창구 기준이고, 투자자 유형별 집계는 별도 TR로 제공됩니다. 두 층을 같은 것으로 합치면 숫자가 어긋납니다.

4. ka10102를 먼저 불러야 하는 TR

ka10039(증권사별매매상위)와 ka10043(거래원매물대분석)은 요청 파라미터에 mmcm_cd(회원사코드)가 필수입니다. 공식 스펙이 설명란에 "회원사 코드는 ka10102 조회"라고 직접 적어 두었습니다. ka10102요청 바디가 아예 없는 TR이라 호출 자체는 간단합니다.

POST https://api.kiwoom.com/api/dostk/stkinfo
api-id: ka10102
authorization: Bearer {access_token}
{}                      // 요청 바디 없음

// 응답
{
  "list": [ { "code": "...", "name": "...", "gb": "..." }, ... ]
}

name의 한글명이 "업종명"으로 붙어 있습니다. 회원사 리스트를 돌려주는 TR인데 공식 명세의 한글명이 업종명이고, 공식 파이썬 예제 list_domestic_brokers.py의 컬럼 매핑에도 "name": "업종명"으로 그대로 들어가 있습니다. 한글명을 기준으로 DataFrame 컬럼을 잡는 코드는 여기서 엉뚱한 이름을 갖게 됩니다. code·name·gb라는 영문 필드명 기준으로 다루는 편이 안전합니다. (표기는 수정될 수 있으므로 현재 가이드로 재확인하십시오.)

실무에서는 ka10102 결과를 하루 한 번 받아 캐시해 두고 ka10040이 내려주는 sel_trde_ori_cd_1~_5(매도거래원코드)와 조인해서 쓰는 형태가 편합니다. ka10002는 코드 필드가 없고 이름만 주므로, 코드로 매칭하려면 ka10040을 써야 합니다.

5. rank_1은 순위가 아니다

ka10038(종목별증권사순위) 응답에서 가장 헷갈리는 자리입니다.

필드실제 내용(한글명)
rank_1기간별 누적 매수량
rank_2기간별 누적 매도량
rank_3기간별 누적 순매수
rank이쪽이 진짜 순위
mmcm_nm회원사명
prid_trde_qty기간중거래량

이름이 rank_N인데 내용은 수량입니다. rank_1을 1등 거래원으로 읽는 코드는 에러 없이 완전히 다른 숫자를 쓰게 됩니다. 실제 순위는 배열 stk_sec_rank 안의 rank 필드입니다.

기간 파라미터도 배타 관계입니다. ka10038dt(기간)에 1:전일 · 4:5일 · 9:10일 · 19:20일 · 39:40일 · 59:60일 · 119:120일을 받는데, 스펙이 "시작일자와 종료일자로 조회를 원하는 경우 기간(dt)값은 빈값('')으로 설정"이라고 명시합니다. strt_dt·end_dtdt같이 채우면 의도와 다른 구간이 나올 수 있습니다. 숫자가 일수와 일치하지 않는다(5일이 4, 10일이 9)는 점도 함께 기억해야 합니다.

반대로 ka10042(순매수거래원순위)는 응답이 rank·mmcm_cd·mmcm_nm 세 개뿐이라 수량이 아예 없습니다. 순위만 필요할 때 쓰는 TR이고, 수량까지 필요하면 ka10038이나 ka10043으로 가야 합니다.

6. 실시간 0F와 이탈 감지

실시간 주식당일거래원 (0F) — FID 번호는 1씩 증가 매도 거래원 매수 거래원 141~145 거래원명 146~150 거래원코드 161~165 수량 166~170 증감 151~155 거래원명 156~160 거래원코드 171~175 수량 176~180 증감 271~275 / 281~285 = 거래원색깔 (HTS 화면용)
0F의 FID는 항목별로 5칸씩 연속 배치된다 (공식 명세 기준)

WebSocket 실시간 구독0F(주식당일거래원)를 등록하면 위 FID들이 values 안에 담겨 옵니다. 번호가 항목별로 5칸씩 연속이라 파싱은 규칙적으로 짤 수 있습니다.

SELL_NAME, SELL_CODE, SELL_QTY, SELL_DIFF = 141, 146, 161, 166
BUY_NAME,  BUY_CODE,  BUY_QTY,  BUY_DIFF  = 151, 156, 171, 176

def parse_0f(values: dict, n: int = 5):
    rows = []
    for i in range(n):
        rows.append({
            "sell_name": values.get(str(SELL_NAME + i)),
            "sell_code": values.get(str(SELL_CODE + i)),
            "sell_qty":  values.get(str(SELL_QTY  + i)),
            "sell_diff": values.get(str(SELL_DIFF + i)),
            "buy_name":  values.get(str(BUY_NAME  + i)),
            "buy_code":  values.get(str(BUY_CODE  + i)),
            "buy_qty":   values.get(str(BUY_QTY   + i)),
            "buy_diff":  values.get(str(BUY_DIFF  + i)),
        })
    return rows
# 271~275 / 281~285(거래원색깔)는 HTS 표시용이라 봇에서는 쓰지 않는다

"상위 5위 안에 있다가 빠진 거래원"은 별도 TR로 따로 제공됩니다. ka10053(당일상위이탈원요청)sel_scesn_tm(매도이탈시간)· sel_upper_scesn_ori(매도상위이탈원)·buy_scesn_tm·buy_upper_scesn_ori를 돌려주고, 같은 필드가 ka10040 응답에도 들어 있습니다. ka10040을 이미 부르고 있다면 ka10053을 따로 호출할 필요가 없을 수 있습니다호출 수를 아껴야 하는 유량 정책에서는 의미 있는 차이입니다.

7. 거래소 구분 — 종목코드에 접미어가 붙는다

거래원 TR의 stk_cd 설명은 전부 같은 문장을 달고 있습니다 — "거래소별 종목코드 (KRX:039490, NXT:039490_NX, SOR:039490_AL)". 즉 같은 종목이라도 어느 시장의 거래원을 볼지에 따라 코드가 달라집니다.

여기에 ka10037·ka10039·ka10043은 별도로 stex_tp(거래소구분)를 받습니다 — 1:KRX · 2:NXT · 3:통합. KRX·NXT 라우팅을 쓰는 봇이라면 주문을 보낸 시장과 조회하는 시장을 맞춰야 숫자가 어긋나지 않습니다. 2026-09-14 애프터마켓 신설로 거래 시간대가 20시까지 늘어난 만큼 통합(3) 기준으로 볼지 시장별로 볼지도 미리 정해 두는 편이 낫습니다.

정리하면 — 경로는 stkinforkinfo 둘, 거래원은 5개까지, 외국계는 추정합, ka10039·ka10043ka10102 선행, ka10038rank_1은 수량, 실시간은 0F입니다. 이 여섯 개만 알고 붙이면 거래원 층에서 헤맬 일은 거의 없습니다.

자주 묻는 것

ka10002ka10040 중 무엇을 쓰나요?

코드·증감·외국계 추정합·이탈정보가 필요하면 ka10040입니다. ka10002는 거래원 이름과 수량만 주고 코드 필드가 없습니다. 경로도 달라서 ka10002stkinfo, ka10040rkinfo입니다.

왜 다섯 개까지만 나오나요?

응답 스펙이 _1~_5까지만 정의돼 있습니다. 6위 이하는 제공되지 않습니다. 따라서 5개 수량의 합은 전체 거래량이 아니며, 비중을 계산할 때 합계를 분모로 쓰면 안 됩니다.

회원사코드는 어디서 받나요?

ka10102(회원사 리스트)입니다. 요청 바디가 없고 list 배열로 code·name·gb를 줍니다. ⚠ name한글명이 "업종명"으로 붙어 있으니 영문 필드명으로 다루십시오.

외국계 수치를 신호로 써도 되나요?

이 글은 그 판단을 하지 않습니다. 다만 prsm=추정이라는 것과 거래원은 주문을 접수한 창구일 뿐 주체가 아니라는 것은 명확히 알고 써야 합니다. 과거 패턴이 반복된다는 보장도 없습니다.

수급 데이터로 돌아가는 봇을 맡기려면

거래원·프로그램매매·투자자별 수급은 층이 다르고 TR도 흩어져 있습니다.
무엇을 어디서 받아 어떻게 합칠지부터 같이 정리해 드립니다. 24시간 빠른 답변 가능합니다.

무료로 상담하기
본 글은 키움증권 공식 GitHub 저장소(Kiwoom-Securities/Kiwoom-REST-API) main 브랜치의 API 명세 파일과 examples/ 파이썬 예제를 2026년 9월 19일 기준으로 읽어 정리했습니다. 본문의 TR 코드·엔드포인트 경로·요청 파라미터·응답 필드명·FID 번호는 전부 그 시점의 공식 표기이며, 실계좌 호출 결과로 검증한 것이 아닙니다. 응답 예시는 필드 구조를 보이기 위한 것으로 실제 시세 값이 아닙니다. 필드명·파라미터·제공 범위는 공지 후 변경될 수 있으므로 반드시 키움 REST API 공식 가이드의 현재 내용으로 대조하십시오. 거래원 데이터는 주문을 접수한 증권사 창구를 나타낼 뿐 주문 주체를 확정하지 않으며, 외국계 관련 필드는 필드명 그대로 추정값입니다. 이 글은 특정 종목이나 매매 시점에 대한 권유를 담고 있지 않으며, 수익률이나 시장 방향에 대한 어떠한 전망도 하지 않습니다. 알고랩(퀀트웍스)은 투자자문업·투자일임업을 영위하지 않으며, 고객이 정한 규칙을 코드로 구현하는 도구 제작 서비스를 제공합니다. 투자 판단과 그 결과의 책임은 전적으로 투자자 본인에게 있습니다.