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

키움 ka10066 장마감후투자자별매매 — 날짜를 못 넣는다

키움 REST · 시세 2026-10-28 · 약 7분 읽기 · 알고랩 AlgoLab
한 줄 요약

ka10066(장마감후투자자별매매요청)은 시장 전체 종목의 투자자별 매매를 한 번에 목록으로 주는 TR인데, 요청 body가 mrkt_tp·amt_qty_tp·trde_tp·stex_tp 4개뿐이라 날짜도 종목코드도 넣을 수 없습니다. 지난 날짜는 받을 수 없으니 봇이 매일 받아 날짜를 붙여 쌓아야 하고, 특정 종목의 과거 값은 ka10059(dt·stk_cd)로 받아야 합니다. 장중 TR ka10064와 달리 개인(ind_invsr)을 포함한 투자자 12개를 주지만 ka10059의 내외국인(natfor)은 없습니다. trde_tp는 "0"이 순매수이고, 응답 리스트 키 opaf_invsr_trde의 한글명은 명세·공식 예제 모두 "장중투자자별매매차트"로 잘못 적혀 있습니다. (키움 공식 명세·공식 GitHub 예제 기준, 2026-10-28 확인)

목차

  1. ka10066은 무엇을 주나 — 언제 어떤 투자자 TR을 쓰나
  2. 요청·응답 실물
  3. 함정 5가지
  4. 매일 쌓는 코드
  5. 자주 묻는 질문

1. ka10066은 무엇을 주나 — 언제 어떤 투자자 TR을 쓰나

"오늘 외국인과 기관이 많이 산 종목"을 장 마감 후 골라 다음 날 매매 후보로 쓰는 봇은 흔한 구조입니다. 키움 REST API에서 투자자 데이터를 주는 TR은 시세·차트·순위정보·종목정보 메뉴에 흩어져 있고, ka10066은 시세 메뉴(POST /api/dostk/mrkcond)에 있습니다. 종목을 지정하지 않고 시장 전체를 목록으로 주는 것이 특징입니다. 장중 시계열을 주는 짝 TR은 어제 정리한 ka10064 장중투자자별매매차트입니다.

TR메뉴 · URL단위날짜투자자 필드개인
ka10066 장마감후투자자별매매시세 mrkcond시장 전체 · 종목 목록없음12개○ ind_invsr
ka10063 장중투자자별매매시세 mrkcond시장 전체 · 종목 목록없음투자자 1개 선택(invsr)✗
ka10064 장중투자자별매매차트차트 chart종목 1개 · 장중 시각없음8개✗
ka10059 종목별투자자기관별종목정보 stkinfo종목 1개 · 일자dt13개○
ka10061 종목별투자자기관별합계종목정보 stkinfo종목 1개 · 기간 합계strt_dt~end_dt13개○

337개 TR 가운데 투자자별 매매 TR로서 ind_invsr(개인)를 주는 것은 ka10059·ka10060·ka10061·ka10066 네 개이고(미체결 조회 ka10075에도 같은 이름 필드가 있지만 주문 TR입니다), 그중 종목코드 없이 시장 전체를 주는 것은 ka10066뿐입니다. 그 대가로 날짜를 고를 수 없습니다.

09:00 15:30 마감 다음 날 이후 장중 (잠정) ka10063 · ka10064 · ka10065 개인 없음 장 마감 후 ka10066 시장 전체 · 날짜 없음 날짜 지정 ka10059 · ka10060 · ka10061 종목 1개씩 ka10066을 놓친 날은 ka10059로 종목마다 다시 받아야 한다
키움 REST 투자자 TR 사용 시점 · 공식 명세(kiwoom-rest-api main 953e5db) 기준

2. 요청·응답 실물

헤더는 다른 키움 REST TR과 같습니다 — api-id: ka10066, authorization: Bearer ..., 연속조회 시 cont-yn·next-key. body 4개는 모두 필수(required: Y)입니다.

POST https://api.kiwoom.com/api/dostk/mrkcond
api-id: ka10066
Content-Type: application/json;charset=UTF-8

{
    "mrkt_tp": "000",
    "amt_qty_tp": "1",
    "trde_tp": "0",
    "stex_tp": "3"
}
필드한글명값 (명세 원문)
mrkt_tp시장구분000 전체 · 001 코스피 · 101 코스닥
amt_qty_tp금액수량구분1 금액 · 2 수량
trde_tp매매구분0 순매수 · 1 매수 · 2 매도
stex_tp거래소구분1 KRX · 2 NXT · 3 통합 (원문 표기 "3.통합")

명세의 응답 예시입니다(리스트 키 opaf_invsr_trde, 일부 필드 생략).

{
    "opaf_invsr_trde": [
        {"stk_cd": "005930", "stk_nm": "삼성전자", "cur_prc": "-7410", "pre_sig": "5",
         "pred_pre": "-50", "flu_rt": "-0.67", "trde_qty": "8",
         "ind_invsr": "0", "frgnr_invsr": "0", "orgn": "0", "fnnc_invt": "0", ...},
        {"stk_cd": "005930", "stk_nm": "삼성전자", "cur_prc": "542", "pre_sig": "3",
         "pred_pre": "0", "flu_rt": "0.00", "trde_qty": "0", ...}
    ],
    "return_code": 0,
    "return_msg": "정상적으로 처리되었습니다"
}

응답 필드는 종목 정보 7개(stk_cd·stk_nm·cur_prc·pre_sig·pred_pre·flu_rt·trde_qty)와 투자자 12개입니다 — ind_invsr 개인, frgnr_invsr 외국인, orgn 기관계, fnnc_invt 금융투자, insrnc 보험, invtrt 투신, etc_fnnc 기타금융, bank 은행, penfnd_etc 연기금등, samo_fund 사모펀드, natn 국가, etc_corp 기타법인. 투자자 필드 단위는 명세에 "백만원, 1주"로 함께 적혀 있어 amt_qty_tp가 1이면 백만원, 2면 주로 읽으면 됩니다.

3. 함정 5가지

① 날짜·종목이 없다 — 놓친 날은 ka10059로 메워야 한다

요청에 dt·base_dt·stk_cd가 없고 응답에도 날짜 필드가 없습니다. 명세상 "어제의 장 마감 후 투자자별 매매"를 다시 받을 방법이 없습니다. 봇이 PC 재부팅이나 휴장일 판정 실수로 하루를 건너뛰면 그날 시장 전체 목록은 영영 비고, 필요한 종목만 ka10059에 dt·stk_cd를 넣어 하나씩 다시 받아야 합니다. 종목 수만큼 호출이 늘어나므로 키움 REST 429 호출 제한을 같이 고려해야 합니다. 그래서 4장 코드는 받은 날짜를 반드시 붙여 저장하고, 휴장일에는 호출하지 않게 휴장일 판정을 앞에 둡니다.

② "장마감후"의 범위는 명세에 없다

명세 개요는 "장마감 후 투자자별 매매 정보를 조회합니다" 한 줄이 전부입니다. 정규장 확정치를 마감 후에 주는 것인지, 장 마감 후 시간외 거래분을 따로 주는 것인지는 적혀 있지 않습니다. 예시 응답의 trde_qty가 "8"·"0"처럼 매우 작다는 점은 시간외 쪽을 떠올리게 하지만 예시는 테스트값이라(③) 근거로 삼을 수 없습니다. 실계좌에서 쓰기 전에 같은 날 같은 종목을 ka10059(dt=오늘)로도 받아 frgnr_invsr·orgn이 같은지 한 번 대조해 보세요. 같으면 정규장 확정치, 크게 다르면 다른 범위입니다. 어느 쪽이든 그 결과를 주석으로 코드에 남겨 두면 나중에 헷갈리지 않습니다.

③ 예시 응답은 테스트값 — 같은 종목이 두 번 나온다

예시의 두 행은 모두 005930 삼성전자인데 현재가가 -7410과 542로 다릅니다. 실제 가격일 수 없는 값이니 테스트 데이터로 봐야 합니다. 다만 이 예시를 그대로 단위 테스트에 넣고 {row["stk_cd"]: row for row in rows}처럼 종목코드로 딕셔너리를 만들면 첫 행이 조용히 덮어써진다는 점은 짚어 둘 만합니다. 실데이터에서 stex_tp="3"(통합)일 때 KRX·NXT 행이 따로 오는지도 명세에 없으므로, 종목코드 중복을 감지해 로그로 남기는 편이 안전합니다(4장 코드).

④ 리스트 키 한글명이 틀려 있다 — opaf_invsr_trde

응답 리스트 opaf_invsr_trde의 한글명이 명세에 "장중투자자별매매차트"로 적혀 있습니다. ka10064 리스트(opmr_invsr_trde_chart)의 한글명과 같은 문구가 복사된 것으로 보이며, 337개 중 opaf_로 시작하는 응답 키는 ka10066 하나뿐입니다. 공식 예제 examples/국내주식/시세/get_domestic_after_close_investor_trade.py도 TABLE_KEYS = {"opaf_invsr_trde": "장중투자자별매매차트"}로 같은 이름을 씁니다. 예제 결과를 엑셀로 내보내면 시트 이름이 "장중"으로 찍히니, 장중 데이터와 섞어 저장하는 구조라면 이름을 직접 바꿔 두세요. 같은 예제는 MAX_PAGES = 10, REQUEST_DELAY_SECONDS = 0.2로 연속조회를 돌립니다 — 시장 전체 목록이 10페이지를 넘으면 잘린다는 뜻이므로, 마지막 응답 헤더의 cont-yn이 Y로 남아 있는지 확인해야 합니다(연속조회 next-key 처리).

⑤ trde_tp 0이 순매수, 내외국인은 없다

ka10066은 ka10064·ka10059·ka10060과 같이 "0"이 순매수입니다. 반면 ka10065는 "1"이 순매수, ka10058은 "1"이 순매도라서 상수 하나를 TR 여러 개에 같이 쓰면 오류 없이 다른 값을 받습니다. 또 ka10059·ka10060·ka10061에 있는 natfor(내외국인)가 ka10066에는 없어서, ka10059로 쌓던 테이블에 ka10066 결과를 이어 붙이면 그 열만 비게 됩니다. 마지막으로 stex_tp 설명의 "3.통합"은 콜론 대신 마침표로 적힌 표기인데, 같은 표기가 337개 중 34개 TR에 반복됩니다. 값은 "3"입니다. NXT 포함 여부가 왜 중요한지는 NXT·KRX 주문 라우팅 글을 보세요.

4. 매일 쌓는 코드

장 마감 후 한 번 호출해 숫자로 바꾸고, 받은 날짜를 붙이고, 종목코드 중복을 감지합니다. cur_prc는 부호가 전일 대비 방향을 뜻하므로(pre_sig 5=하락과 "-7410") 가격으로 쓸 때는 절댓값을 씁니다.

import pandas as pd

INVESTORS = ["ind_invsr", "frgnr_invsr", "orgn", "fnnc_invt", "insrnc", "invtrt",
             "etc_fnnc", "bank", "penfnd_etc", "samo_fund", "natn", "etc_corp"]   # 12개, natfor 없음
NET_BUY = "0"                                       # ka10066 전용 (ka10065는 "1")

def to_num(v):
    s = str(v).strip().replace(",", "")
    neg = s.startswith("-")
    s = s.lstrip("+-")
    return -float(s or 0) if neg else float(s or 0)

def parse_ka10066(rows: list, trade_date: str) -> pd.DataFrame:
    """rows = 모든 페이지의 opaf_invsr_trde를 이어 붙인 것, trade_date = 'YYYY-MM-DD'"""
    df = pd.DataFrame(rows)
    if df.empty:
        return df
    dup = df["stk_cd"].duplicated(keep=False)
    if dup.any():
        print("중복 종목코드:", sorted(df.loc[dup, "stk_cd"].unique()))
    df["price"] = df["cur_prc"].map(to_num).abs()   # 부호는 방향, 가격은 절댓값
    for c in INVESTORS + ["trde_qty"]:
        df[c] = df[c].map(to_num)
    df.insert(0, "date", trade_date)                # 응답에 날짜가 없으니 직접 붙인다
    return df[["date", "stk_cd", "stk_nm", "price", "pre_sig", "trde_qty"] + INVESTORS]

body = {"mrkt_tp": "000", "amt_qty_tp": "1", "trde_tp": NET_BUY, "stex_tp": "3"}

명세의 응답 예시를 그대로 넣어 돌린 출력입니다.

중복 종목코드: ['005930']
         date  stk_cd   price pre_sig  trde_qty  ind_invsr  frgnr_invsr  orgn
0  2026-10-28  005930  7410.0       5       8.0        0.0          0.0   0.0
1  2026-10-28  005930   542.0       3       0.0        0.0          0.0   0.0

이 결과를 ka10066_20261028.csv처럼 날짜 파일로 저장하거나 SQLite 테이블에 (date, stk_cd) 키로 넣어 두면, 몇 달 뒤 "외국인·기관 동시 순매수 다음 날" 같은 수급 조건을 백테스트할 재료가 됩니다. 수급 조건을 필터로 쓰는 설계는 수급 데이터로 봇 필터 만들기에 정리했습니다.

5. 자주 묻는 질문

ka10066으로 지난 날짜의 투자자별 매매를 받을 수 있나요?

공식 명세상으로는 받을 수 없습니다. 요청 body가 mrkt_tp·amt_qty_tp·trde_tp·stex_tp 4개뿐이고 날짜 파라미터가 없습니다. 지난 날짜가 필요하면 ka10059에 dt와 stk_cd를 넣어 종목마다 받아야 하므로, ka10066은 매일 받아 날짜를 붙여 저장해 두는 것이 맞습니다.

ka10066에 종목코드를 넣어 한 종목만 받을 수 있나요?

없습니다. ka10066 요청에는 stk_cd가 없고 시장 전체 종목을 목록으로 돌려줍니다. 한 종목만 필요하면 응답에서 걸러 쓰거나, 처음부터 종목 단위 TR인 ka10059·ka10060을 쓰세요.

ka10066에 개인 순매수가 있나요?

있습니다. 응답에 ind_invsr(개인투자자)를 포함해 외국인·기관계·금융투자·보험·투신·기타금융·은행·연기금등·사모펀드·국가·기타법인까지 투자자 12개가 옵니다. 장중 ka10064에는 개인이 없다는 점과 다릅니다. 다만 ka10059에 있는 내외국인(natfor)은 없습니다.

ka10066의 trde_tp는 몇을 넣어야 순매수인가요?

0이 순매수, 1이 매수, 2가 매도입니다. 같은 투자자 데이터라도 ka10065는 1이 순매수, ka10058은 1이 순매도라서 상수를 TR마다 따로 두는 것이 안전합니다.

수급 데이터를 쌓는 봇, 만들어 드립니다

ka10066·ka10059로 외국인·기관·개인 매매를 매일 받아 쌓고, 다음 날 매매 필터로 쓰는 키움 REST 봇을 맞춤 제작합니다. 24시간 빠른 답변 가능합니다.

무료 상담 시작하기