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

키움 ka10060 종목별투자자기관별차트 — 합은 0이다

키움 REST · 차트 2026-11-01 · 약 6분 읽기 · 알고랩 AlgoLab
한 줄 요약

ka10060(종목별투자자기관별차트요청)은 종목 하나의 날짜별 개인·외국인·기관계와 기관 세부 7곳(금융투자·보험·투신·기타금융·은행·연기금등·사모펀드)·국가·기타법인·내외국인 순매수를 주는 TR이고, 요청 5필드는 ka10059와 똑같습니다. 다른 점은 URL(/api/dostk/chart), 응답 리스트 키(stk_invsr_orgn_chart), 그리고 등락기호·등락률·누적거래량 3칸이 없다는 것입니다. 공식 응답 예시를 검산하면 순매수 기준 ind_invsr + frgnr_invsr + orgn + etc_corp = 0이 두 행 모두 맞아, 받은 데이터의 단위·부호 검증에 그대로 쓸 수 있습니다. 값은 전부 문자열이라 pandas에서 숫자로 바꾸지 않고 sum()하면 '60195646'처럼 이어 붙습니다. (키움 공식 명세 kiwoom_api_spec.json·공식 GitHub 예제, 저장소 main 953e5db 기준, 2026-11-01 확인)

목차

  1. ka10059와 무엇이 같고 무엇이 다른가
  2. 요청·응답 실물
  3. 검산 — 투자자 합은 0이다
  4. 함정 5가지
  5. 받아서 저장하는 코드
  6. 자주 묻는 질문

1. ka10059와 무엇이 같고 무엇이 다른가

메뉴 위치는 국내주식 > 차트 > 종목별투자자기관별차트요청, 주소는 POST https://api.kiwoom.com/api/dostk/chart입니다. 일봉 ka10081, 장중 투자자 차트 ka10064와 같은 URL을 쓰고 헤더 api-id: ka10060으로만 구분됩니다. 이름이 거의 같은 ka10059 종목별투자자기관별요청은 /api/dostk/stkinfo에 붙어 있습니다. 공식 명세를 나란히 놓으면 이렇습니다.

항목ka10059 종목별투자자기관별ka10060 종목별투자자기관별차트
URL/api/dostk/stkinfo/api/dostk/chart
요청 bodydt·stk_cd·amt_qty_tp·trde_tp·unit_tp — 5개 모두 필수, 값 정의도 동일
응답 리스트 키stk_invsr_orgnstk_invsr_orgn_chart
가격 칸cur_prc·pre_sig·pred_pre·flu_rtcur_prc·pred_pre만 (pre_sig·flu_rt 없음)
거래 칸acc_trde_qty·acc_trde_pricaacc_trde_prica만 (acc_trde_qty 없음)
투자자 칸13개 동일 — ind_invsr·frgnr_invsr·orgn·fnnc_invt·insrnc·invtrt·etc_fnnc·bank·penfnd_etc·samo_fund·natn·etc_corp·natfor
natn 단위 설명공란"단위: 백만원"

결론부터 말하면 투자자 데이터만 필요하면 둘은 바꿔 써도 되고, 등락률이나 거래량(주)을 같이 저장하려면 ka10059가 낫습니다. ka10060으로 받고 거래량이 필요해지면 ka10081 일봉을 한 번 더 불러야 합니다. 반대로 ka10060이 쓸모 있는 경우는 ka10059의 natn처럼 단위 설명이 빈 칸을 대조할 때입니다. 기관 합계와 외국인만 기간으로 받으려면 ka10045 종목별기관매매추이가 더 가볍습니다.

2. 요청·응답 실물

POST https://api.kiwoom.com/api/dostk/chart
Content-Type: application/json;charset=UTF-8
api-id: ka10060
authorization: Bearer {접근토큰}
cont-yn: (연속조회 시 응답 헤더 값)
next-key: (연속조회 시 응답 헤더 값)

{
    "dt": "20241107",
    "stk_cd": "005930",
    "amt_qty_tp": "1",
    "trde_tp": "0",
    "unit_tp": "1000"
}

값 정의는 명세 원문 그대로 amt_qty_tp 1:금액·2:수량, trde_tp 0:순매수·1:매수·2:매도, unit_tp 1000:천주·1:단주입니다. stk_cd는 KRX 039490, NXT 039490_NX, SOR(통합) 039490_AL처럼 접미사로 거래소를 고릅니다. 공식 응답 예시입니다.

{
    "stk_invsr_orgn_chart": [
        {"dt": "20241107", "cur_prc": "+61300", "pred_pre": "+4000",
         "acc_trde_prica": "1105968", "ind_invsr": "1584", "frgnr_invsr": "-61779",
         "orgn": "60195", "fnnc_invt": "25514", "insrnc": "0", "invtrt": "0",
         "etc_fnnc": "34619", "bank": "4", "penfnd_etc": "-1", "samo_fund": "58",
         "natn": "0", "etc_corp": "0", "natfor": "1"},
        {"dt": "20241106", "cur_prc": "+74800", "pred_pre": "+17200",
         "acc_trde_prica": "448203", "ind_invsr": "-639", "frgnr_invsr": "-7",
         "orgn": "646", "fnnc_invt": "-47", "insrnc": "15", "invtrt": "-2",
         "etc_fnnc": "730", "bank": "-51", "penfnd_etc": "1", "samo_fund": "0",
         "natn": "0", "etc_corp": "0", "natfor": "0"}
    ],
    "return_code": 0,
    "return_msg": "정상적으로 처리되었습니다"
}

dt는 하루만 넣지만 응답은 그날부터 과거로 여러 날짜가 리스트로 옵니다. 더 받을 게 있으면 응답 헤더 cont-yn: Y와 next-key가 오고, 그 두 값을 다음 요청 헤더에 넣어 이어 받습니다. 공식 예제 get_domestic_stock_investor_by_institution_chart.py는 MAX_PAGES = 10, 요청 간격 REQUEST_DELAY_SECONDS = 0.2초로 이 반복을 구현합니다.

3. 검산 — 투자자 합은 0이다

순매수(trde_tp=0)는 누가 사면 누가 팔았다는 뜻이라, 시장 전체로는 합이 0이어야 합니다. 공식 예시 두 행을 숫자로 바꿔 더해 봤습니다(pandas 2.2.3).

          ind_invsr  frgnr_invsr   orgn  etc_corp  natn  natfor
20241107       1584       -61779  60195         0     0       1
20241106       -639           -7    646         0     0       0

ind_invsr + frgnr_invsr + orgn + etc_corp  →  0, 0
orgn  vs  기관 세부 7곳 합(fnnc_invt~samo_fund)  →  60195 vs 60194,  646 vs 646
ka10060 공식 예시 20241107 (단위: 백만원, 순매수) 개인 +1,584 외국인 −61,779 기관계 +60,195 = 0 기관계 내역 금융투자 25,514 · 기타금융 34,619 사모펀드 58 · 은행 4 · 연기금등 −1 보험 0 · 투신 0 → 합 60,194 기관계와 1 차이 = 백만원 반올림 출처: kiwoom_api_spec.json ka10060 response_example 검산 (막대 길이는 대략)
개인 + 외국인 + 기관계 + 기타법인 = 0, 기관계 ≈ 세부 7곳 합

이 항등식이 실무에서 쓸모 있는 이유는 파싱 검증입니다. 부호를 잘못 떼었거나(abs()를 투자자 칸에까지 적용), 칸을 밀려 읽었거나, 단위를 섞었으면 합이 0에서 크게 벗어납니다. 백만원 단위 반올림 때문에 ±몇 정도는 생기므로 저장할 때 abs(합) ≤ 5 정도로 경고를 걸어 두면 됩니다.

두 가지는 예시로 확정할 수 없었습니다. natfor(내외국인)는 첫 행이 1인데 합 0에 넣으면 1이 되므로 합에서 빼는 것이 맞아 보입니다. natn(국가)은 예시가 두 행 모두 0이라 기관계 안에 들어가는지 따로인지 판별되지 않습니다. 실제 데이터에서 natn이 0이 아닌 날이 나오면 한 번 확인하세요.

4. 함정 5가지

① 값이 전부 문자열이다. 명세 type이 전부 String이고 예시도 "1584"처럼 따옴표가 붙어 옵니다. 공식 예제도 pd.DataFrame(records)로 그대로 만들어 dtype이 object입니다. 이 상태로 df["orgn"].sum()을 하면 덧셈이 아니라 문자열 이어 붙이기가 됩니다 — 실측 결과 '60195646'. 반드시 pd.to_numeric을 먼저 거치세요. "+61300"처럼 + 부호가 붙은 문자열도 to_numeric은 정상 변환합니다.

② 공식 예제의 천 단위 쉼표는 6개 칸만 찍힌다. 예제의 NUMERIC_COLUMNS에는 국가·금융투자·기타금융·누적거래대금·연기금등·현재가 6개만 있고, 그마저 6자리 이상일 때만 쉼표를 넣습니다. 개인·외국인·기관계는 빠져 있습니다. 화면 표시용이라 계산에는 영향이 없지만, 출력 화면을 보고 "이 칸은 숫자가 아닌가?" 헷갈리기 쉽습니다. _format_display()를 거친 값은 쉼표 문자열이니 계산에 쓰면 안 됩니다.

③ 수량 모드여도 명세 설명은 "백만원"이다. 투자자 13칸과 acc_trde_prica의 설명이 모두 "단위: 백만원"으로 적혀 있고, amt_qty_tp=2(수량)일 때의 단위는 따로 적혀 있지 않습니다. 수량이면 unit_tp(1000:천주, 1:단주)를 따르는 것이 자연스럽지만 명세상 근거는 없습니다. 수량으로 처음 받을 때 같은 날 HTS 투자자별 매매동향 화면과 한 번 대조하세요. 같은 이름의 amt_qty_tp가 TR마다 0/1·1/2로 다르게 정의된 문제는 ka10059 글에 표로 정리했습니다.

④ cur_prc에 부호가 붙는다. "부호가 포함된 숫자"라서 하락일에는 -가 붙어 옵니다. 가격으로 쓸 때는 abs(), 방향은 pred_pre 부호로 판단합니다. ka10059에 있는 pre_sig(등락기호)가 ka10060에는 없으므로 방향 판단은 pred_pre 하나뿐입니다. abs()는 가격 칸에만 쓰고 투자자 칸에는 절대 쓰지 마세요 — 순매도의 -가 사라집니다.

⑤ 예시 숫자를 테스트 기대값으로 쓰지 말 것. 11월 6일 cur_prc가 +74800인데 다음 날 +61300에 pred_pre가 +4000입니다. 74,800원에서 4,000원 올라 61,300원이 될 수는 없으니 예시는 형식을 보여 주는 값입니다. 단위 테스트는 실제 호출 결과를 저장해 만드세요.

5. 받아서 저장하는 코드

import time, requests, pandas as pd

URL = "https://api.kiwoom.com/api/dostk/chart"
NUM = ["cur_prc", "pred_pre", "acc_trde_prica", "ind_invsr", "frgnr_invsr", "orgn",
       "fnnc_invt", "insrnc", "invtrt", "etc_fnnc", "bank", "penfnd_etc",
       "samo_fund", "natn", "etc_corp", "natfor"]

def fetch_ka10060(token, stk_cd, dt, amt_qty_tp="1", trde_tp="0", unit_tp="1000", max_pages=10):
    headers = {"Content-Type": "application/json;charset=UTF-8",
               "api-id": "ka10060", "authorization": f"Bearer {token}"}
    body = {"dt": dt, "stk_cd": stk_cd, "amt_qty_tp": amt_qty_tp,
            "trde_tp": trde_tp, "unit_tp": unit_tp}
    rows = []
    for _ in range(max_pages):
        r = requests.post(URL, headers=headers, json=body, timeout=10)
        j = r.json()
        if j.get("return_code") != 0:
            raise RuntimeError(f'{j.get("return_code")} {j.get("return_msg")}')
        rows += j.get("stk_invsr_orgn_chart", [])
        if r.headers.get("cont-yn") != "Y":
            break
        headers["cont-yn"] = "Y"
        headers["next-key"] = r.headers.get("next-key", "")
        time.sleep(0.2)                      # 공식 예제와 같은 간격

    df = pd.DataFrame(rows)
    df[NUM] = df[NUM].apply(pd.to_numeric)   # 문자열 → 숫자 (안 하면 sum이 이어 붙기)
    df["price"] = df["cur_prc"].abs()        # 부호는 가격 칸에서만 뗀다
    if trde_tp == "0":                       # 순매수일 때만 합 0 검산
        gap = df["ind_invsr"] + df["frgnr_invsr"] + df["orgn"] + df["etc_corp"]
        bad = df[gap.abs() > 5]
        if not bad.empty:
            print("검산 경고:", bad[["dt"]].assign(gap=gap).to_dict("records"))
    return df.drop_duplicates("dt")

DB 키는 (stk_cd, dt, amt_qty_tp, trde_tp)로 잡아야 금액·수량, 순매수·매수·매도를 섞어 받아도 덮어쓰지 않습니다. 토큰 발급과 키움 REST 전체 구조는 키움 REST API 자동매매 가이드에, 수급 데이터를 매매 필터로 쓰는 방법은 체결강도·거래원·프로그램매매로 봇 필터 만들기에 있습니다.

6. 자주 묻는 질문

ka10060과 ka10059는 무엇이 다른가요?

요청 body 5개(dt, stk_cd, amt_qty_tp, trde_tp, unit_tp)와 투자자 13칸은 같습니다. ka10060은 URL이 /api/dostk/chart, 응답 리스트 키가 stk_invsr_orgn_chart이고, ka10059에 있는 pre_sig(등락기호)·flu_rt(등락률)·acc_trde_qty(누적거래량) 3칸이 없습니다. 거래량까지 함께 저장하려면 ka10059가 편합니다.

ka10060 투자자별 값을 더하면 왜 0이 되나요?

순매수(trde_tp=0)는 한쪽이 사면 다른 쪽이 판 것이라 시장 전체 합이 0입니다. 공식 응답 예시에서 ind_invsr + frgnr_invsr + orgn + etc_corp가 두 행 모두 0이었습니다. 백만원 단위 반올림으로 몇 정도 오차는 날 수 있으니, 저장할 때 합의 절댓값이 5를 넘으면 경고하도록 두면 파싱 실수를 잡을 수 있습니다.

pandas로 orgn 합계를 냈더니 이상한 큰 숫자가 나옵니다.

응답 값이 전부 문자열이라 숫자로 바꾸지 않고 sum()을 하면 문자열이 이어 붙습니다. 공식 예시 두 행에서 df['orgn'].sum()은 '60195646'이 됩니다. pd.to_numeric으로 먼저 변환하세요. '+61300'처럼 플러스 부호가 붙은 값도 정상 변환됩니다.

amt_qty_tp를 2(수량)로 하면 단위는 무엇인가요?

명세에는 투자자 칸 단위가 모두 '백만원'으로만 적혀 있고 수량일 때의 단위는 따로 없습니다. unit_tp(1000:천주, 1:단주)를 따르는 것이 자연스럽지만 명세상 근거가 없으므로, 처음 수량으로 받을 때 같은 날 HTS 투자자별 매매동향 화면과 한 번 대조해 확인하세요.

기관·외국인 수급을 쌓는 봇, 만들어 드립니다

ka10060·ka10059·ka10045로 종목별 투자자 순매수를 매일 받아 단위·부호를 검산해 저장하고, 매매 필터로 쓰는 키움 REST 봇을 맞춤 제작합니다. 24시간 빠른 답변 가능합니다.

무료 상담 시작하기