키움 프로그램매매 API 8종 — 시장코드 4체계
ka90003·ka90004·ka90005·ka90006·ka90007·ka90008·ka90010·ka90013)인데,
시장을 지정하는 mrkt_tp가 TR마다 네 가지 코드 체계를 씁니다.
P00101/P10102를 쓰는 TR, 거래소구분에 따라 P001_NX01로 바뀌는 TR,
0/1을 쓰는 TR, 000/001/101을 쓰는 TR이 공존합니다.
게다가 ka90010 명세에는 코스닥 자리에 코스피 접두사(P001_AL02)가 적혀 있고,
ka90003의 응답 예시는 아예 ka90005의 것입니다.
상수 하나를 공유하는 순간 조용히 틀린 시장의 데이터가 쌓입니다.
이 글에서 다루는 것
- 프로그램매매 TR 8종과 경로 3개
mrkt_tp네 가지 체계 — 전수 대조표ka90010명세의 접두사 오기ka90003의 응답 예시가 다른 TR의 것이다- 단위 — 백만원·천만원, 1주·1000주
- 일자별 TR에 분·틱 구분이 필수인 이유
stex_tp가 없는 두 TR- 차익 vs 비차익, 그리고
basis - 이웃 TR 둘 —
ka90009·ka90012
1. 프로그램매매 TR 8종과 경로 3개
이 글은 키움 REST API 순위 조회(rkinfo)에서
다루지 않은 프로그램매매 계열만 떼어낸 글입니다. 순위 허브가 ka10020~ka10032
대역을 다룬 반면, ka90xxx 대역은 통째로 비어 있었습니다.
| TR | 이름 | 경로 | 핵심 파라미터 |
|---|---|---|---|
ka90003 | 프로그램순매수상위50 | /stkinfo | trde_upper_tp amt_qty_tp mrkt_tp stex_tp |
ka90004 | 종목별 프로그램매매현황 | /stkinfo | dt mrkt_tp stex_tp |
ka90005 | 프로그램매매추이(시간대별) | /mrkcond | date amt_qty_tp mrkt_tp min_tic_tp stex_tp |
ka90006 | 프로그램매매 차익잔고추이 | /mrkcond | date stex_tp |
ka90007 | 프로그램매매 누적추이 | /mrkcond | date amt_qty_tp mrkt_tp stex_tp |
ka90008 | 종목 시간별 프로그램매매추이 | /mrkcond | amt_qty_tp stk_cd date |
ka90010 | 프로그램매매추이(일자별) | /mrkcond | date amt_qty_tp mrkt_tp min_tic_tp stex_tp |
ka90013 | 종목 일별 프로그램매매추이 | /mrkcond | amt_qty_tp stk_cd date |
경로가 두 갈래(/api/dostk/stkinfo·/api/dostk/mrkcond)로 갈리고,
바로 옆의 ka90009는 /api/dostk/rkinfo에 있습니다.
이름이 ka90으로 시작한다고 한 경로에 모아 두면 오류코드 1504
("해당 URI에서는 지원하는 API ID가 아닙니다")를 받습니다.
토큰·헤더 규칙 자체는 키움 REST API 자동매매 가이드와 동일합니다.
2. mrkt_tp 네 가지 체계 — 전수 대조표
가장 많이 막히는 지점입니다. 여덟 개 TR과 이웃 ka90009까지 명세 원문의
mrkt_tp 설명을 그대로 옮기면 이렇습니다.
| TR | 코스피 | 코스닥 | 비고 |
|---|---|---|---|
ka90003 ka90004 | P00101 | P10102 | 고정 |
ka90005 ka90010 | P00101 / P001_NX01 / P001_AL01 | P10102 / P101_NX02 / P101_AL02 | stex_tp 값(1·2·3)에 따라 바뀜 |
ka90007 | 0 | 1 | 한 자리 숫자 |
ka90009 | 001 | 101 | 전체는 000 |
ka90008 ka90013 | 파라미터 없음 — stk_cd로 종목을 직접 지정 | ||
여기서 진짜 위험한 것은 에러가 아니라 에러가 나지 않는 경우입니다. 형식이 맞는 값을 엉뚱한 TR에 넣으면 요청은 통과하고 다른 시장의 데이터나 빈 배열이 돌아옵니다. TR별 매핑 딕셔너리를 따로 두는 것 말고는 방법이 없습니다.
MRKT_TP = {
"ka90003": {"kospi": "P00101", "kosdaq": "P10102"},
"ka90004": {"kospi": "P00101", "kosdaq": "P10102"},
"ka90007": {"kospi": "0", "kosdaq": "1"},
"ka90009": {"all": "000", "kospi": "001", "kosdaq": "101"},
}
# ka90005 / ka90010 은 stex_tp 와 짝으로 결정된다
MRKT_TP_BY_STEX = {
("ka90005", "1"): {"kospi": "P00101", "kosdaq": "P10102"},
("ka90005", "2"): {"kospi": "P001_NX01", "kosdaq": "P101_NX02"},
("ka90005", "3"): {"kospi": "P001_AL01", "kosdaq": "P101_AL02"},
}
3. ka90010 명세의 접두사 오기
위 딕셔너리를 명세에서 그대로 베끼면 ka90010에서 사고가 납니다.
ka90005와 ka90010은 같은 설명문을 공유하는데, ka90010 쪽 코스닥 통합 값만 다릅니다.
# ka90005 명세 원문
코스닥- 거래소구분값 1일경우:P10102, 2일경우:P101_NX02, 3일경우:P101_AL02
# ka90010 명세 원문 ← 마지막 값이 P001_ 로 시작한다
코스닥- 거래소구분값 1일경우:P10102, 2일경우:P101_NX02, 3일경우:P001_AL02
P001은 코스피 접두사입니다. 코스닥 자리에 코스피 코드가 적혀 있는 셈이라
둘 중 하나는 오기입니다. 어느 쪽이 서버에서 유효한지는 명세만으로 확정할 수 없으므로,
통합(stex_tp=3) + 코스닥 조합은 두 값을 모두 한 번씩 호출해 응답을 대조한 뒤 상수로 박으십시오.
이 글은 어느 쪽이 맞는지 단정하지 않습니다 — 실호출로 확인하지 않았습니다.
거래소 구분 자체가 최근에 생긴 축입니다. stex_tp의 2:NXT는
대체거래소 도입으로 들어온 값이고, 프로그램매매 TR 전반에 이 파라미터가 붙어 있습니다.
주문 쪽 라우팅과 같은 개념이며 NXT·KRX 주문 라우팅에서
따로 다뤘습니다. 수급 데이터를 볼 때도 어느 거래소 기준의 집계인지를 먼저 고정해야 시계열이 섞이지 않습니다.
4. ka90003의 응답 예시가 다른 TR의 것이다
명세를 열어 응답 예시부터 보고 파서를 짜는 습관이 있다면 여기서 정확히 한 번 당합니다.
ka90003(프로그램순매수상위50)의 필드 정의와 응답 예시가 서로 다른 TR의 것입니다.
# ka90003 의 "응답 필드 정의" — 순위 목록
prm_netprps_upper_50[] : rank, stk_cd, stk_nm, cur_prc, flu_sig,
pred_pre, flu_rt, acc_trde_qty,
prm_sell_amt, prm_buy_amt, prm_netprps_amt
# ka90003 에 붙어 있는 "응답 예시" — 실제로는 ka90005 의 것
{"prm_trde_trnsn": [{
"cntr_tm": "170500", "dfrt_trde_sel": "0", "dfrt_trde_buy": "0",
"ndiffpro_trde_netprps": "+17", "all_netprps": "+17",
"kospi200": "+47839", "basis": "-146.5..."
}]}
배열 키부터 다릅니다(prm_netprps_upper_50 vs prm_trde_trnsn).
예시를 기준으로 짠 코드는 KeyError도 아니고 빈 리스트를 조용히 반환하기 쉽습니다 —
resp.get("prm_trde_trnsn", []) 같은 방어 코드가 오히려 문제를 감춥니다.
필드 정의 표를 기준으로 짜고, 첫 호출의 실제 응답을 로그로 남겨 대조하십시오.
5. 단위 — 백만원·천만원, 1주·1000주
명세가 필드마다 단위를 적어 두는데, 같은 개념인데 TR별로 다릅니다.
| TR | 금액 단위 | 수량 단위 |
|---|---|---|
ka90003 | 백만원 (prm_netprps_amt) | 1주 (acc_trde_qty) |
ka90004 | 백만원 | 1000주 (buy_cntr_qty) |
ka90005 ka90006 | 백만원 | 1000주 |
ka90013 | 백만원 | 1주 (prm_sell_qty) |
ka90009 | 천만원 | 1000주 |
ka90012 | 백만원 | 1주 |
수량에서 1,000배, 금액에서 10배가 어긋날 수 있고
둘 다 int()가 성공하므로 로그에는 아무것도 남지 않습니다.
키움 금현물 API에서도 같은 유형을 만났는데,
거기서 쓴 검산법이 여기서도 그대로 통합니다 — 같은 응답 안에서 수량 × 가격이 금액과 자릿수가 맞는지 한 번 계산해 보면
명세의 단위 주석보다 빠르고 확실합니다.
ka90004에는 하나 더 있습니다. 합계 필드 이름이 tot_1~tot_6이고
의미는 설명 칸에만 적혀 있습니다(tot_1 매수체결수량합계, tot_2 매수체결금액합계,
tot_3 매도체결수량합계, tot_4 매도체결금액합계, tot_5 순매수대금합계).
tot_6은 한글명이 "합계6"이고 설명이 비어 있으며 예시값도 빈 문자열입니다 —
쓰지 마십시오.
6. 일자별 TR에 분·틱 구분이 필수인 이유
ka90010은 이름이 "프로그램매매추이요청 일자별"인데
min_tic_tp(분틱구분, 0:틱 / 1:분)가 필수입니다.
시간대별인 ka90005에도 같은 파라미터가 필수이고, 두 TR의 요청 예시는
ka90005가 "1", ka90010이 "0"으로 서로 다릅니다.
이름과 파라미터가 어긋나 보이지만, 둘이 사실상 같은 조회의 두 모드이고
집계 축만 다른 구조로 읽힙니다. 실무적으로 중요한 것은 하나입니다 —
빼면 1511(필수 입력 값이 존재하지 않습니다)로 떨어지므로 값을 반드시 넣어야 하고,
어느 값이 어떤 집계를 주는지는 두 값을 모두 호출해 응답 행 수와 cntr_tm 간격으로 판정하십시오.
명세 문구만으로는 확정되지 않습니다.
7. stex_tp가 없는 두 TR
여덟 개 중 ka90008(종목 시간별)과 ka90013(종목 일별)에는
stex_tp 파라미터가 아예 없습니다. 대신 종목코드에 접미사를 붙여 거래소를 지정합니다.
# ka90008 / ka90013 의 stk_cd 설명 원문
거래소별 종목코드 (KRX:039490, NXT:039490_NX, SOR:039490_AL)
# 그런데 요청 예시는 접미사가 없다
{"amt_qty_tp": "1", "stk_cd": "005930", "date": "20241125"}
즉 거래소를 지정하는 방법이 TR 그룹별로 둘입니다 —
파라미터(stex_tp)와 종목코드 접미사(_NX·_AL).
공통 요청 빌더가 stex_tp를 자동으로 붙이면 ka90008에서
정의되지 않은 파라미터를 보내게 되고, 반대로 접미사 로직을 빼면 통합 기준 데이터를 못 받습니다.
ka90013의 응답에는 거꾸로 stex_tp가 들어 있는데
값이 숫자가 아니라 한글 문자열 "통합"입니다.
요청은 1/2/3, 응답은 KRX/NXT/통합이라
왕복이 대칭이 아닙니다. 같은 값으로 되돌려 보낼 수 없으니 변환표가 필요합니다.
8. 차익 vs 비차익, 그리고 basis
ka90005·ka90006의 응답이 이 계열의 핵심입니다. 접두사로 갈립니다.
dfrt_trde_*— 차익거래(선물·현물 가격차를 노린 거래)ndiffpro_trde_*— 비차익거래(바스켓 매매 등 그 외)all_sel/all_buy/all_netprps— 전체 합계
각각 매도·매수·순매수가 금액과 수량 두 벌로 옵니다(dfrt_trde_sel와
dfrt_trde_sell_qty가 따로 있습니다). 접두사 하나 차이라 파싱에서 가장 헷갈리는 지점이고,
금액 계열과 수량 계열의 단위가 다르다는 점(백만원 vs 1000주)이 겹칩니다.
같은 응답에 kospi200과 basis가 함께 오는 것도 이 구조 때문입니다.
차익거래는 선물과 현물의 가격차에서 나오므로 지수와 베이시스가 같이 있어야 맥락이 됩니다.
예시값은 kospi200: "+47839", basis: "-146.5..." 형태입니다 —
지수 필드는 소수점이 제거된 형태로 보이므로 스케일을 실호출로 확인하십시오.
ka90006은 여기서 잔고 축만 떼어 buy_dfrt_trde_qty·sel_dfrt_trde_amt와
전일 대비 증감(*_irds_amt)을 일자별로 줍니다.
수급 데이터는 예측 장치가 아닙니다. 차익잔고나 프로그램 순매수의 방향으로 지수나 개별 종목의 향방을 알 수 있다는 뜻이 아니며, 이 글은 그런 주장을 하지 않습니다. 이 숫자들은 이미 일어난 거래의 집계입니다. 수급 조건을 봇의 필터로 넣으면 백테스트 표본이 줄어 통계적 신뢰도가 떨어지는 비용이 함께 발생합니다 — 임계값을 정하기 전에 그 필터가 표본에 무엇을 하는지부터 확인하십시오. 과거 데이터에 기반한 어떤 계산도 미래 성과를 보장하지 않습니다.
9. 이웃 TR 둘 — ka90009·ka90012
번호는 붙어 있는데 성격이 다른 둘입니다.
ka90009 외국인기관매매상위는 경로가
/api/dostk/rkinfo, 즉 순위 조회(rkinfo) 그룹 소속입니다.
응답 구조가 독특한데, 배열 한 원소 안에 네 개의 순위가 가로로 붙어 옵니다 —
외인순매도·외인순매수·기관순매도·기관순매수 각각에 대해
*_stk_cd·*_stk_nm·*_amt·*_qty 네 필드씩 열여섯 개입니다.
"행 = 순위"이므로 네 개의 독립된 표로 쪼개서 써야 합니다.
같은 성격의 데이터를 다른 증권사로 받는 방법은
KIS 외국인·기관 순매수 API에 정리해 뒀습니다.
ka90012 대차거래내역은 프로그램매매가 아니라 대차입니다.
dbrt_trde_cntrcnt(체결주수)·dbrt_trde_rpy(상환주수)·rmnd(잔고주수)로
단순하지만, 헷갈리는 지점이 하나 있습니다 — ka90013(프로그램매매) 응답에도
dbrt_trde_rpy_sum·remn_rcvord_sum이라는 대차 관련 필드가 섞여 있고
명세 예시에서는 빈 문자열입니다. 이름이 비슷해 ka90012의 값과 혼동하기 쉽습니다.
대차·공매도 쪽 데이터는 공매도 추이 ka10014가
더 직접적입니다.
10. 정리 — 이 여덟 개를 쓸 때의 규칙
다섯 줄로 줄이면 이렇습니다.
mrkt_tp는 TR별 딕셔너리로. 공통 상수 금지.ka90010의 코스닥 통합 값은 두 후보를 모두 호출해 확정.- 응답 예시가 아니라 필드 정의 표로 파서를 짠다.
- 수량·금액은 수량 × 가격 = 금액 검산으로 단위를 판정.
ka90008·ka90013은stex_tp대신 종목코드 접미사.
여덟 개를 매일 돌리면 호출 수가 금방 늘어납니다. 명세 오류코드에
1700(API별 유량 초과)과 1702(그룹 요청 개수 초과)가 따로 있으므로
호출 한도를 먼저 계산하고,
일자별 TR은 하루 한 번만 받아 저장하는 쪽이 맞습니다.
자주 묻는 질문
키움 REST API에서 프로그램매매 데이터는 어떤 TR로 받나요?
여덟 개입니다 — ka90003(순매수상위50), ka90004(종목별 현황),
ka90005(시간대별), ka90006(차익잔고), ka90007(누적),
ka90008(종목 시간별), ka90010(일자별), ka90013(종목 일별).
경로는 ka90003·ka90004가 /api/dostk/stkinfo, 나머지가 /api/dostk/mrkcond입니다.
mrkt_tp에 무엇을 넣어야 하나요?
TR에 따라 다릅니다. ka90003·ka90004는 P00101/P10102,
ka90005·ka90010은 stex_tp에 따라 P001_NX01 등으로 바뀌고,
ka90007은 0/1, ka90009는 000/001/101입니다.
상수를 공유하면 형식 오류가 나거나, 더 나쁘게는 요청이 통과하면서 빈 결과가 돌아옵니다.
차익거래와 비차익거래는 무엇이 다른가요?
응답 접두사로 갈립니다. 차익은 dfrt_trde_*, 비차익은 ndiffpro_trde_*이고
전체 합계가 all_*로 따로 옵니다. 같은 응답에 kospi200과 basis가 오는 것도
차익거래가 선물·현물 가격차에서 나오기 때문입니다.
다만 이 수치로 방향을 예측할 수 있다는 뜻은 아닙니다 — 이미 일어난 거래의 집계입니다.
ka90003의 응답 예시를 그대로 파싱하면 되나요?
안 됩니다. 필드 정의는 prm_netprps_upper_50 배열의 순위 목록인데
같은 문서에 붙은 응답 예시는 prm_trde_trnsn, 즉 ka90005의 것입니다.
예시가 다른 TR의 것으로 잘못 붙어 있습니다.
필드 정의 표를 기준으로 짜고 첫 호출의 실제 응답으로 대조하십시오.
수량 필드에 1000을 곱해야 하나요?
TR마다 다릅니다. ka90004·ka90005·ka90006은 1000주,
ka90003·ka90013·ka90012는 1주입니다.
금액은 대부분 백만원인데 ka90009만 천만원입니다.
같은 응답 안에서 수량 × 가격이 금액과 자릿수가 맞는지 계산해 보는 것이 가장 확실합니다.
수급 데이터를 봇에 붙이기 전에
단위·시장코드·거래소 구분은 한 번 틀리면 몇 달치 시계열이 조용히 오염됩니다.
어디를 고정하고 어디를 실호출로 확인할지 같이 정리해 드립니다. 24시간 빠른 답변 가능합니다.
ka90003~ka90013 대역 원문을 대조해 작성했습니다.
파라미터·필드·단위 표기는 변경될 수 있으며, 본문에서 지적한 명세 불일치(ka90010 접두사, ka90003 응답 예시)는
문서 원문 대조로 확인한 것이고 실호출로 검증하지 않았습니다 — 어느 값이 유효한지는 단정하지 않았습니다.
반드시 키움증권 개발자 포털의 현재 명세와 실제 응답으로 대조하십시오.
수급 데이터는 이미 체결된 거래의 집계이며, 이 글은 시장의 방향이나 수익률에 대한 어떠한 전망도 담고 있지 않습니다.
과거 데이터에 기반한 계산은 미래 성과를 보장하지 않습니다.
알고랩(퀀트웍스)은 투자자문업·투자일임업을 영위하지 않으며, 고객이 정한 규칙을 코드로 구현하는 도구 제작 서비스를 제공합니다.
투자 판단과 그 결과의 책임은 전적으로 투자자 본인에게 있습니다.