AlgoLab Blog · 한국투자증권 KIS · 실시간 WebSocket · 2026

KIS 실시간 시세 TR — H0STCNT0는 KRX 전용

KIS · 실시간시세 2026-09-17 · 약 8분 읽기 · 알고랩 AlgoLab
한 줄 요약 KIS 실시간 체결가로 가장 널리 쓰이는 H0STCNT0KRX 전용입니다. NXT(넥스트레이드)에서 일어난 체결은 이 TR로 들어오지 않습니다. NXT는 H0NXCNT0, 두 시장을 합친 통합은 H0UNCNT0으로 TR이 따로 있습니다. 규칙은 단순합니다 — TR ID의 셋째·넷째 글자가 시장을 가릅니다: ST=KRX · NX=NXT · UN=통합 · UP=지수. 그런데 시장만 바꿔 끼우면 파서가 깨집니다. 체결가는 46필드로 같지만 22번째 필드 이름이 다르고(CCLD_DVSNCNTG_CLS_CODE), 호가는 필드 수 자체가 59개와 65개로 다릅니다. 아래는 한국투자증권 공식 저장소 open-trading-api의 예제 소스 원문으로 국내주식 실시간 25종을 전수 대조한 결과입니다.

목차

  1. 거래량이 조금씩 모자란 봇
  2. TR ID 8자리를 읽는 법
  3. 국내주식 실시간 25종 전수표
  4. 함정 1 — 46필드 중 22번만 이름이 다르다
  5. 함정 2 — 호가는 필드 수가 다르다
  6. 함정 3 — 대소문자가 TR마다 갈린다
  7. 함정 4 — 시간외·지수에는 시장 구분이 없다
  8. 시장 중립으로 짜는 법
  9. 자주 묻는 질문

거래량이 조금씩 모자란 봇

넥스트레이드(NXT) 출범 이후 국내 주식은 같은 종목이 두 곳에서 체결됩니다. 주문을 어디로 보낼지는 이미 NXT·KRX 주문 라우팅 쪽에서 다룬 주제인데, 실시간 시세를 받는 쪽에서 더 조용한 문제가 생깁니다.

증상은 이렇게 나타납니다. 봇은 멀쩡히 돌아갑니다. 웹소켓도 끊기지 않고, 체결 데이터도 계속 들어옵니다. 그런데 누적 거래량이 HTS 화면보다 조금씩 적습니다. 거래량 급증을 조건으로 쓰는 전략이라면 신호가 늦게 뜨거나 아예 안 뜹니다. 체결강도(CTTR)를 쓰는 전략이면 값이 미묘하게 다릅니다.

원인은 구독한 TR ID 한 글자입니다. H0STCNT0은 공식 예제에서 ccnl_krx국내주식 실시간체결가 (KRX) 함수에 할당돼 있습니다. KRX에서 일어난 체결만 보내줍니다. NXT 체결은 들어오지 않습니다. 그런데 응답 형식이 완전히 정상이라 로그만 봐서는 아무 문제가 없어 보입니다.

이 글은 연결하는 방법에 관한 글이 아닙니다. approval_key 발급, AES CBC 복호화, PINGPONG 처리 같은 접속 절차는 KIS API 웹소켓 실시간 시세 편에서 이미 다뤘습니다. 여기서 답하는 질문은 하나입니다 — 어느 TR을 구독해야 내가 원하는 데이터가 오는가.

TR ID 8자리를 읽는 법

KIS 실시간 TR ID는 8자리이고 자리마다 의미가 정해져 있습니다. 공식 예제 25개의 tr_id 상수를 전부 뽑아 놓고 보면 규칙이 한눈에 드러납니다.

TR ID 8자리 구조 H0 UN CNT 0 고정 시장 데이터 종류 고정 ST KRX NX NXT UN 통합 UP 지수 CNT 체결가 ASP 호가 ANC 예상체결 PGM 프로그램 MBC 회원사 MKO 장운영 H0STCNT0 = KRX 체결가 · H0UNCNT0 = 통합 체결가
KIS 실시간 TR ID — 셋째·넷째 글자가 시장을 가른다

이 규칙을 알면 H0UNASP0통합 호가, H0NXPGM0NXT 프로그램매매로 바로 읽힙니다. 반대로 내가 지금 구독 중인 TR이 어느 시장인지도 셋째 글자만 보면 즉시 확인됩니다.

지금 당장 확인할 것. 운영 중인 봇의 구독 코드에서 H0ST로 시작하는 TR을 찾아보십시오. 체결가·호가·예상체결·프로그램매매·회원사·장운영정보 중 하나라도 H0ST를 쓰고 있다면 그 데이터는 KRX만 담고 있습니다.

국내주식 실시간 25종 전수표

공식 저장소 examples_llm/domestic_stock/ 아래의 실시간 함수 25개를 전부 열어 tr_id 상수와 columns 리스트를 뽑은 결과입니다. 필드 수는 예제가 정의한 컬럼 개수를 그대로 센 값입니다.

데이터KRXNXT통합필드 수
체결가H0STCNT0H0NXCNT0H0UNCNT046 / 46 / 46
호가H0STASP0H0NXASP0H0UNASP059 / 65 / 65
예상체결H0STANC0H0NXANC0H0UNANC045 / 46 / 46
프로그램매매H0STPGM0H0NXPGM0H0UNPGM011 / 11 / 11
회원사H0STMBC0H0NXMBC0H0UNMBC078 / 78 / 78
장운영정보H0STMKO0H0NXMKO0H0UNMKO011 / 11 / 10

여기에 시장 구분이 없는 7종이 더 있습니다.

구분TR ID내용필드 수
시간외
(KRX만)
H0STOUP0시간외 실시간체결가43
H0STOAA0시간외 실시간호가54
H0STOAC0시간외 실시간예상체결43
지수
(UP 접두)
H0UPCNT0국내지수 실시간체결30
H0UPANC0국내지수 실시간예상체결30
H0UPPGM0국내지수 실시간프로그램매매88
체결통보H0STCNI0 / H0STCNI9실시간체결통보 (실전 / 모의)26

함정 1 — 46필드 중 22번만 이름이 다르다

체결가 세 TR은 필드 수가 46개로 완전히 같습니다. 순서도 같습니다. 딱 하나, 22번째 필드의 이름만 다릅니다.

# ccnl_krx.py — H0STCNT0 (KRX)
columns = [
    "MKSC_SHRN_ISCD", "STCK_CNTG_HOUR", "STCK_PRPR", "PRDY_VRSS_SIGN",
    ...
    "SELN_CNTG_SMTN", "SHNU_CNTG_SMTN",
    "CCLD_DVSN",          # ← 22번째
    "SHNU_RATE", ...
]

# ccnl_nxt.py — H0NXCNT0 (NXT)  /  ccnl_total.py — H0UNCNT0 (통합)
columns = [
    "MKSC_SHRN_ISCD", "STCK_CNTG_HOUR", "STCK_PRPR", "PRDY_VRSS_SIGN",
    ...
    "SELN_CNTG_SMTN", "SHNU_CNTG_SMTN",
    "CNTG_CLS_CODE",      # ← 22번째 (이름만 다름)
    "SHNU_RATE", ...
]

의미는 같은 체결구분입니다. 그런데 이름이 다르기 때문에, 응답을 딕셔너리로 만들어 놓고 row["CCLD_DVSN"]으로 읽던 코드는 통합(H0UNCNT0)으로 바꾸는 순간 KeyError가 납니다.

역으로 이건 좋은 소식이기도 합니다. 나머지 45개 필드가 이름·순서 모두 같으므로, 22번 하나만 흡수하면 KRX 파서를 통합에 거의 그대로 재사용할 수 있습니다. MKSC_SHRN_ISCD(종목코드) · STCK_PRPR(현재가) · CNTG_VOL(체결량) · ACML_VOL(누적거래량) · CTTR(체결강도) · VI_STND_PRC(VI 기준가)는 위치와 이름이 동일합니다.

함정 2 — 호가는 필드 수가 다르다

체결가와 달리 호가는 필드 개수 자체가 다릅니다. KRX H0STASP059개, NXT H0NXASP0과 통합 H0UNASP065개입니다. 늘어난 여섯 개는 전부 중간가 관련 항목입니다.

추가 필드읽는 법
KMID_PRCKRX 중간가
KMID_TOTAL_RSQNKRX 중간가 총잔량
KMID_CLS_CODEKRX 중간가 구분코드
NMID_PRCNXT 중간가
NMID_TOTAL_RSQNNXT 중간가 총잔량
NMID_CLS_CODENXT 중간가 구분코드

위치 기반 파서를 쓰고 있다면 여기서 조용히 깨집니다. 실시간 응답은 ^ 구분자로 이어진 한 줄 문자열로 들어옵니다. KRX 기준 59칸으로 인덱스를 박아 둔 코드에 NXT 응답을 먹이면 예외가 나지 않고 값만 어긋납니다. 호가 잔량 자리에서 엉뚱한 숫자를 읽고, 그 숫자로 지정가를 계산하면 주문이 이상한 가격에 나갑니다.

10호가 잔량으로 지정가를 정하는 방식은 KIS API 호가 조회 편에서 다뤘는데, 그 로직을 실시간으로 옮길 때 시장별 필드 수를 먼저 확인해야 하는 이유가 이것입니다.

함정 3 — 대소문자가 TR마다 갈린다

공식 예제의 columns 리스트를 25개 전부 비교하면 대소문자가 일정하지 않습니다.

표기해당 TR
대문자체결가 3종(H0STCNT0·H0NXCNT0·H0UNCNT0) · 호가 3종 · 체결통보 · NXT/통합의 예상체결·프로그램매매·회원사·장운영정보
소문자KRX의 예상체결 H0STANC0 · 프로그램매매 H0STPGM0 · 회원사 H0STMBC0 · 장운영정보 H0STMKO0 · 시간외 3종 · 지수 3종

가장 헷갈리는 조합은 예상체결입니다. 같은 데이터인데 시장에 따라 표기와 개수가 둘 다 바뀝니다.

# exp_ccnl_krx.py — H0STANC0 (KRX 예상체결) : 소문자 45개
columns = [
    "mksc_shrn_iscd",  # 유가증권단축종목코드
    "stck_cntg_hour",  # 주식체결시간
    "stck_prpr",       # 주식현재가
    ...
]

# exp_ccnl_nxt.py — H0NXANC0 (NXT 예상체결) : 대문자 46개
columns = [
    "MKSC_SHRN_ISCD",
    "STCK_CNTG_HOUR",
    "STCK_PRPR",
    ...
]

해법은 간단합니다 — 컬럼명을 하드코딩하지 마십시오. 공식 예제 함수는 전부 (msg, columns) 두 개를 돌려줍니다. 돌려받은 columns를 그대로 써서 매핑하면 대소문자·개수 차이를 코드가 알아서 흡수합니다. 정 하드코딩이 필요하면 최소한 key.upper()로 정규화한 뒤 접근하십시오.

함정 4 — 시간외·지수에는 시장 구분이 없다

앞의 매트릭스에서 3열이 채워지는 것은 여섯 종류뿐입니다. 나머지는 시장 구분 자체가 존재하지 않습니다.

참고로 지수 프로그램매매 H0UPPGM0은 필드가 88개로 국내주식 실시간 중 가장 많습니다. 종목 단위 프로그램매매(H0STPGM0, 11필드)와 전혀 다른 크기라 같은 파서로 처리할 수 없습니다.

시장 중립으로 짜는 법

구독 TR을 설정값으로 빼 두면 시장 전환이 한 줄로 끝납니다. 아래는 앞서 확인한 네 가지 차이를 전부 흡수하는 구조입니다.

import os

# 시장 코드 → TR 접두사. 셋째·넷째 글자만 갈린다.
MARKET_PREFIX = {"KRX": "H0ST", "NXT": "H0NX", "UNIFIED": "H0UN"}

# 데이터 종류 → 접두사 뒤에 붙는 세 글자
FEED_SUFFIX = {
    "ccnl":    "CNT0",   # 체결가
    "asking":  "ASP0",   # 호가
    "exp":     "ANC0",   # 예상체결
    "program": "PGM0",   # 프로그램매매
    "member":  "MBC0",   # 회원사
    "mkop":    "MKO0",   # 장운영정보
}

# 기본값을 통합으로 둔다. NXT 누락을 막는 가장 싼 방법이다.
MARKET = os.getenv("KIS_MARKET", "UNIFIED")

def tr_id(feed: str, market: str = MARKET) -> str:
    """예: tr_id("ccnl") -> 'H0UNCNT0',  tr_id("asking", "KRX") -> 'H0STASP0'"""
    return MARKET_PREFIX[market] + FEED_SUFFIX[feed]

# 체결구분 필드는 시장에 따라 이름이 다르다 (KRX만 CCLD_DVSN)
def contract_division(row: dict):
    for key in ("CNTG_CLS_CODE", "CCLD_DVSN", "cntg_cls_code", "ccld_dvsn"):
        if key in row:
            return row[key]
    return None

def to_dict(msg_fields: list, columns: list) -> dict:
    """예제가 돌려준 columns를 그대로 써서 대소문자·개수 차이를 흡수한다."""
    keys = [c.upper() for c in columns]
    return dict(zip(keys, msg_fields))

핵심은 세 가지입니다.

  1. 기본값을 UNIFIED로 둡니다. 명시적으로 KRX만 보겠다고 정하지 않는 한 통합을 받는 편이 누락을 막습니다.
  2. 체결구분은 네 가지 키를 모두 시도합니다. 대소문자 두 갈래 × 이름 두 갈래를 한 함수에 가둡니다.
  3. 필드 매핑은 반드시 columns로 합니다. 호가 59 ↔ 65처럼 개수가 달라져도 인덱스가 밀리지 않습니다.

모의투자는 따로 확인하십시오. 공식 예제에서 env_dv(실전/모의) 인자를 받는 실시간 함수는 ccnl_krx·asking_price_krx·ccnl_notice처럼 KRX 계열과 체결통보이고, NXT·통합 함수에는 그 인자가 없습니다. 또 env_dv를 받는 경우에도 체결가·호가는 실전과 모의의 tr_id가 같고, 실제로 값이 갈리는 것은 체결통보뿐입니다(H0STCNI0H0STCNI9). 모의 환경에서 NXT·통합 실시간이 어떻게 동작하는지는 예제 소스만으로 단정할 수 없으므로 모의투자에서 실전 전환 절차와 함께 KIS Developers 공식 문서로 확인하십시오.

정리

실시간 시세에서 시장 구분은 연결이 되느냐 마느냐의 문제가 아니라, 데이터가 맞느냐의 문제입니다. 틀린 TR을 써도 에러는 나지 않습니다. 그래서 더 오래갑니다.

자동매매를 어떤 증권사로 붙일지 고르는 단계라면 증권사 API 비교키움 실시간 0B·0D 쪽도 같이 보십시오. 실시간 항목 구성은 증권사마다 꽤 다릅니다.

자주 묻는 질문

H0STCNT0를 구독하면 NXT 체결도 들어오나요?

들어오지 않습니다. 공식 저장소 예제에서 H0STCNT0ccnl_krx, 즉 국내주식 실시간체결가 (KRX)에 할당돼 있습니다. NXT는 H0NXCNT0, 통합은 H0UNCNT0입니다. 셋 다 응답 필드가 46개라 데이터가 정상으로 보이고, 누락은 거래량이 실제보다 적게 잡히는 형태로만 드러납니다.

KIS 실시간 TR ID는 어떻게 읽나요?

H0 + 시장 2자 + 데이터 3자 + 0 구조입니다. 시장은 ST(KRX)·NX(NXT)·UN(통합)·UP(지수), 데이터는 CNT(체결가)·ASP(호가)·ANC(예상체결)·PGM(프로그램매매)·MBC(회원사)·MKO(장운영정보)입니다. 시간외 3종과 체결통보는 이 규칙에서 벗어나므로 개별 확인이 필요합니다.

KRX와 NXT의 응답 필드가 같은가요?

체결가는 46개로 같지만 22번째 이름이 CCLD_DVSN(KRX) ↔ CNTG_CLS_CODE(NXT·통합)로 다릅니다. 호가는 개수가 다릅니다 — KRX 59개, NXT·통합 65개이고 늘어난 여섯은 KMID_PRC·KMID_TOTAL_RSQN·KMID_CLS_CODE·NMID_PRC·NMID_TOTAL_RSQN·NMID_CLS_CODE입니다. 위치로 읽는 파서는 여섯 칸이 어긋납니다.

실시간 응답 필드명이 대문자인가요 소문자인가요?

TR마다 다릅니다. 체결가·호가·체결통보는 대문자, KRX의 예상체결·프로그램매매·회원사·장운영정보와 시간외·지수는 소문자입니다. 같은 예상체결인데 H0STANC0는 소문자 45개, H0NXANC0는 대문자 46개입니다. 예제가 돌려주는 columns 리스트를 그대로 받아 쓰는 편이 안전합니다.

시간외와 지수도 NXT가 있나요?

공식 예제 목록 기준으로는 없습니다. 시간외는 H0STOUP0·H0STOAA0·H0STOAC0 세 종목뿐이고 전부 KRX 접두사입니다. 지수는 UP 접두사로 H0UPCNT0·H0UPANC0·H0UPPGM0 세 종목이 있고 여기에도 시장 구분이 없습니다. 항목 구성과 운영 정책은 바뀔 수 있으므로 구축 전에 KIS Developers 공식 문서에서 현재 기준을 다시 확인하십시오.

실시간 데이터부터 꼬여 있다면

어느 TR을 구독해야 하는지, 통합으로 갈지 시장별로 나눌지는
전략이 무엇을 보느냐에 따라 달라집니다. 24시간 빠른 답변 가능합니다.

자동매매 제작 문의하기
본 글의 TR ID·필드명·필드 수는 2026년 9월 17일 기준 한국투자증권 공식 저장소 koreainvestment/open-trading-apiexamples_llm/domestic_stock/ 예제 소스를 직접 대조해 정리한 것입니다. 필드 수는 예제가 정의한 columns 리스트의 길이를 센 값이며, 실제 응답과 다를 수 있습니다. API 항목 구성·필드·운영 정책은 예고 없이 변경될 수 있으므로 실제 구축 전 KIS Developers 공식 문서로 현재 기준을 반드시 확인하십시오. 모의투자 환경에서의 NXT·통합 실시간 지원 여부는 예제 소스만으로 확정되지 않아 단정하지 않았습니다. 이 글은 API 사용 방법에 관한 기술 문서이며 특정 종목·시장에 대한 투자 판단을 제공하지 않습니다. 알고랩(퀀트웍스)은 투자자문업·투자일임업을 영위하지 않으며, 고객이 지정한 규칙을 자동화하는 도구를 제작·납품합니다. 투자 판단과 그 결과는 이용자 본인에게 귀속됩니다.