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

키움 ka10035 외인연속순매매상위 — tot는 3일 합계다

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

ka10035(외인연속순매매상위요청)는 "외국인이 며칠째 사고 있나"를 알려 주지 않습니다. 응답 리스트 for_cont_nettrde_upper에는 dm1·dm2·dm3(D-1~D-3 순매매 수량)와 tot(합계)만 있고, 공식 응답 예시 4행 모두 tot = dm1+dm2+dm3였습니다. 대신 ka10131에 없는 두 가지가 있습니다 — 연속 순매도(trde_tp 1)와 시장 전체(mrkt_tp 000). 주의할 점은 trde_tp 방향이 같은 /api/dostk/rkinfo의 ka10037과 반대라는 것, 숫자가 부호 붙은 문자열이라 그대로 정렬하면 순매도 종목이 맨 위로 온다는 것입니다. (공식 명세 kiwoom_api_spec.json·예제, 2026-10-14 main 기준)

목차

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

1. ka10131과 무엇이 다른가 — 한 장 비교

그제 정리한 ka10131 기관·외국인 연속매매에는 큰 빈칸이 둘 있었습니다. 연속 순매도 목록을 못 받고, 코스피·코스닥을 따로 불러야 합니다. 외국인에 한해서는 순위정보 메뉴의 ka10035가 그 빈칸을 메웁니다. 대신 잃는 것도 있습니다.

항목ka10035 외인연속순매매상위ka10131 기관외국인연속매매현황
메뉴 / URL순위정보 · POST /api/dostk/rkinfo기관/외국인 · POST /api/dostk/frgnistt
주체외국인만기관계 · 외국인 · 합계
방향trde_tp 1:연속순매도, 2:연속순매수netslmt_tp 2:순매수(고정값)
시장mrkt_tp 000:전체, 001:코스피, 101:코스닥001·101뿐
기간base_dt_tp 0:당일기준, 1:전일기준dt 1·3·5·10·20·120일 또는 시작/종료일
연속일수없음 — D-1·D-2·D-3·합계frgnr_cont_netprps_dys 등 3개
단위수량(1주)뿐금액(백만원)·수량 둘 다
거래소stex_tp 1:KRX, 2:NXT, 3:통합 (둘 다 필수)

요약하면 ka10035는 "최근 사흘 외국인 방향"을 시장 전체에서 한 번에 보는 TR이고, ka10131은 "며칠째, 얼마어치"를 시장별로 보는 TR입니다. 둘을 같이 쓰는 이유가 여기 있습니다.

2. 요청·응답 실물

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

{
    "mrkt_tp": "000",
    "trde_tp": "2",
    "base_dt_tp": "1",
    "stex_tp": "1"
}

헤더는 다른 키움 REST TR과 같습니다. api-id: ka10035, authorization: Bearer …, 연속조회 시 cont-yn·next-key. 응답은 리스트 하나에 13개 필드입니다. 명세의 response_example 첫 두 행입니다.

{
    "for_cont_nettrde_upper": [
        {
            "stk_cd": "005930",
            "stk_nm": "삼성전자",
            "cur_prc": "10200",
            "pred_pre_sig": "3",
            "pred_pre": "0",
            "dm1": "+33928250",
            "dm2": "+234840",
            "dm3": "+233891",
            "tot": "+34396981",
            "limit_exh_rt": "+71.53",
            "pred_pre_1": "",
            "pred_pre_2": "",
            "pred_pre_3": ""
        },
        {
            "stk_cd": "005930",
            "stk_nm": "삼성전자",
            "cur_prc": "-8540",
            "pred_pre_sig": "5",
            "pred_pre": "-140",
            "dm1": "+4033818",
            "dm2": "+12474308",
            "dm3": "+13173262",
            "tot": "+29681388",
            "limit_exh_rt": "+0.10",
            ...
        },
        ...
    ],
    "return_code": 0,
    "return_msg": "정상적으로 처리되었습니다"
}
element한글명명세 설명
stk_cd · stk_nm종목코드 · 종목명—
cur_prc현재가단위: 원, 부호가 포함된 숫자
pred_pre_sig전일대비기호1:상한가, 2:상승, 3:보합, 4:하한가, 5:하락
pred_pre전일대비단위: 원, 부호가 포함된 숫자
dm1 · dm2 · dm3D-1 · D-2 · D-3단위: 1주, 부호가 포함된 숫자
tot합계단위: 1주, 부호가 포함된 숫자
limit_exh_rt한도소진율단위: %, 부호 포함 소수점 둘째 자리
pred_pre_1~3전일대비1~3설명 없음 (예시는 빈 문자열)

3. 함정 6가지

① 연속일수가 없다 — tot는 사흘치 합계

TR 이름에 "연속"이 들어 있지만 응답에는 며칠째인지 나타내는 필드가 없습니다. tot의 한글명은 "합계"이고, 공식 예시 4행을 직접 더해 보면 전부 세 값의 합과 같습니다.

>>> n = df[["dm1","dm2","dm3","tot"]].apply(pd.to_numeric)
>>> (n.dm1 + n.dm2 + n.dm3 == n.tot).tolist()
[True, True, True, True]

그러니 tot가 큰 종목은 "오래 산 종목"이 아니라 최근 사흘 동안 많이 산 종목입니다. 예시 첫 행처럼 하루(dm1 3,392만 주)가 합계의 99%를 차지하는 경우도 그대로 상위에 옵니다. "5일 이상 연속 순매수" 같은 조건은 이 TR만으로는 만들 수 없고, ka10131의 frgnr_cont_netprps_dys나 종목별 일자 데이터(ka10059)로 직접 세야 합니다. 목록이 어떤 기준으로 정렬돼 오는지, 몇 종목까지 오는지도 명세에 없습니다.

② trde_tp 방향이 옆 TR과 반대다

같은 /api/dostk/rkinfo 경로에 외국인 관련 순위 TR이 셋 있는데, 매매구분 코드의 뜻이 서로 다릅니다.

TR필드123·4
ka10035 외인연속순매매상위trde_tp연속순매도연속순매수—
ka10034 외인기간별매매상위trde_tp순매도순매수3:순매매
ka10037 외국계창구매매상위trde_tp순매수순매도3:매수, 4:매도

ka10037 요청을 복사해 api-id만 ka10035로 바꾸면 trde_tp: "1"이 그대로 남아 순매수를 원했는데 연속 순매도 목록이 옵니다. 오류는 나지 않습니다. 같은 경로에서 api-id로만 TR을 구분하는 구조의 다른 함정은 키움 순위조회 rkinfo 글에 모아 두었습니다. 방향 코드는 상수로 박고 TR별로 따로 두는 편이 안전합니다.

③ 수량뿐이라 저가주가 위로 온다

dm1~tot의 단위는 전부 "1주"입니다. 금액 필드가 없습니다. 1주 1만 원 종목 100만 주와 1주 50만 원 종목 2만 주는 같은 100억 원이지만 tot로 줄을 세우면 앞쪽이 50배 크게 보입니다. 종목 간 비교가 필요하면 cur_prc를 곱해 대략적인 금액으로 바꾸십시오. 사흘 동안 가격이 움직였으므로 정확한 금액은 아니고, 정확한 금액이 필요하면 ka10131(amt_qty_tp 0:금액)을 씁니다.

④ cur_prc의 마이너스는 가격이 아니다

예시 두 번째 행의 cur_prc는 "-8540"입니다. 주가가 음수일 수는 없으니 이 부호는 전일 대비 하락 표시입니다(같은 행의 pred_pre_sig가 5:하락, pred_pre가 −140). 금액 환산이나 주문 가격에 쓸 때는 반드시 abs()를 거칩니다. 키움 REST 시세 응답 전반에 있는 습관이라 키움 REST API 자동매매 가이드의 응답 정규화 함수에 한 번에 넣어 두는 것이 좋습니다.

⑤ 문자열 그대로 정렬하면 순매도가 1등

공식 예제의 NUMERIC_COLUMNS는 '한도소진율'과 '현재가' 두 개뿐이고, 이것도 화면 표시용 쉼표만 넣습니다. dm1~tot는 "+33928250" 같은 문자열로 남습니다. 이 상태로 sort_values를 하면 이렇게 됩니다(pandas 2.2.3).

>>> s = pd.Series(['+4033818', '+33928250', '+234840', '-1200000'])
>>> s.sort_values(ascending=False).tolist()
['-1200000', '+4033818', '+33928250', '+234840']
>>> pd.to_numeric(s).sort_values(ascending=False).tolist()
[33928250, 4033818, 234840, -1200000]

문자 코드상 -가 +보다 뒤라서 내림차순 맨 위에 순매도 종목이 오고, 나머지도 자릿수와 무관하게 첫 숫자로 줄을 섭니다. "상위 5개만 매수 후보"로 자르면 엉뚱한 종목이 들어갑니다. 변환은 pd.to_numeric으로 하고, 변환 뒤 isna().sum()을 로그로 남겨 두면 형식이 바뀌었을 때 바로 알 수 있습니다.

⑥ 명세가 비워 둔 것들

4. 최소 호출 코드

토큰은 환경변수에서 읽고 코드에 앱키를 쓰지 않습니다. 연속조회는 공식 예제와 같이 최대 10페이지(MAX_PAGES = 10), 0.2초 간격(REQUEST_DELAY_SECONDS = 0.2)으로 받습니다. 헤더 처리는 cont-yn·next-key 글과 같습니다.

import os, time, requests
import pandas as pd

BASE  = "https://api.kiwoom.com"            # 모의: https://mockapi.kiwoom.com
TOKEN = os.environ["KIWOOM_TOKEN"]
SELL_STREAK, BUY_STREAK = "1", "2"          # ka10035 전용 — ka10037과 반대
NUM = ["dm1", "dm2", "dm3", "tot", "limit_exh_rt"]

def ka10035(trde_tp=BUY_STREAK, mrkt_tp="000", base_dt_tp="1", stex_tp="1",
            max_pages=10, delay=0.2):
    body = {"mrkt_tp": mrkt_tp, "trde_tp": trde_tp,
            "base_dt_tp": base_dt_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": "ka10035"}
        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"ka10035 {j.get('return_code')}: {j.get('return_msg')}")
        rows += j.get("for_cont_nettrde_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["price"] = pd.to_numeric(df["cur_prc"], errors="coerce").abs()   # 부호 = 등락 표시
    df["approx_krw"] = df["tot"] * df["price"]                          # 사흘치 대략 금액
    return df

buy = ka10035(BUY_STREAK).sort_values("approx_krw", ascending=False)
print("NaN after parse:", int(buy[NUM].isna().sum().sum()))

return_code가 0이 아닐 때의 의미는 키움 return_code 글에, 호출이 몰렸을 때의 429 처리는 키움 429 레이트리밋 글에 있습니다.

5. 봇에서 쓰는 순서

ka10035는 매수 신호가 아니라 제외 목록과 후보 목록을 같이 만드는 체로 쓰는 것이 자연스럽습니다.

  1. 장 마감 뒤 ka10035를 trde_tp 1(연속순매도)로 한 번 불러 제외 목록을 만든다 — ka10131로는 못 하는 부분입니다.
  2. 같은 TR을 trde_tp 2로 불러 approx_krw 기준 후보를 추린다. 시장 전체(000)라 한 번이면 됩니다.
  3. 후보 중 "며칠째"가 필요한 종목만 ka10131(frgnr_cont_netprps_dys)이나 ka10059로 확인한다.
  4. 한국투자증권 계좌라면 같은 역할을 하는 KIS 쪽 TR이 다르니 KIS 외국인·기관 순매수 글을 보십시오.

외국인 수급은 해석이 갈리는 데이터라 이 목록 자체가 매매 근거가 되지는 않습니다. 이 글은 데이터를 정확히 받는 방법을 다루며 특정 종목이나 전략을 권하지 않습니다.

6. 자주 묻는 질문

ka10035로 외국인이 며칠째 연속 순매수했는지 알 수 있나요?

명세상으로는 알 수 없습니다. 응답에 연속일수 필드가 없고 D-1·D-2·D-3 세 날의 수량과 합계 tot만 있습니다. 공식 응답 예시 4행은 모두 tot가 세 값의 합과 정확히 같았습니다. 4일 이상 연속인지 세려면 ka10131의 frgnr_cont_netprps_dys를 쓰거나 종목별 일자 데이터로 직접 세야 합니다.

ka10035와 ka10131은 무엇이 다른가요?

ka10035는 순위정보(/api/dostk/rkinfo)에 있고 외국인만, 연속 순매도·순매수 둘 다, 시장 전체(000) 조회가 됩니다. 단위는 수량(1주)뿐입니다. ka10131은 기관/외국인(/api/dostk/frgnistt)에 있고 기관·외국인·합계를 다 주지만 순매수 고정이며 시장 전체가 없습니다. 연속일수와 금액은 ka10131에만 있습니다.

trde_tp에 1을 넣으면 순매수인가요?

ka10035에서는 아닙니다. 1이 연속순매도, 2가 연속순매수입니다. 같은 순위 경로의 ka10037(외국계창구매매상위)은 반대로 1이 순매수, 2가 순매도입니다. 요청 body를 복사해 api-id만 바꾸면 오류 없이 반대 방향 목록이 옵니다.

모의투자 서버에서도 ka10035를 호출할 수 있나요?

명세에는 모의투자 도메인 https://mockapi.kiwoom.com이 운영 도메인과 함께 적혀 있습니다. 다만 모의 서버의 순위 데이터가 운영과 같은지는 명세에 없으므로, 필터 기준값을 정할 때는 운영 응답으로 한 번 확인하는 것이 안전합니다.

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

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

무료 상담 시작하기