상세 컨텐츠

본문 제목

키움 OpenAPI 조회 및 실시간 데이터 처리 — 대체거래소(NXT) 종목코드부터 C# 예제까지

주식 자동매매 시스템/Kiwoom OpenAPI

by 별을 보는 사람 2026. 6. 4. 07:00

본문

반응형

안녕하세요! 이번에는 키움 OpenAPI에서 시세 데이터를 조회하고 실시간으로 받아보는 방법을 정리해볼게요. 특히 2024년부터 달라진 대체거래소(넥스트레이드) 지원 내용도 함께 다루겠습니다.

조회의 기본 흐름, 다시 한번 짚고 가기

키움 OpenAPI의 모든 조회는 동일한 패턴을 따릅니다.

SetInputValue() → CommRqData() → OnReceiveTrData → GetCommData()

SetInputValue()로 입력값을 세팅하고, CommRqData()로 서버에 요청을 보내면, OnReceiveTrData 이벤트가 발생하고, 그 안에서 GetCommData()로 데이터를 꺼내는 구조죠. 이건 어떤 TR을 쓰든 똑같으니 이 흐름만 확실히 잡아두세요.

대체거래소 — 종목코드가 달라졌어요

넥스트레이드(NXT) 대체거래소가 도입되면서, 같은 종목이라도 어느 거래소 데이터를 조회하느냐에 따라 종목코드가 달라집니다. 예를 들어 키움증권(039490)의 경우 이렇게 나뉘어요.

종목코드 구분 (예: 039490)

"039490" → KRX (한국거래소) — 기존과 동일
"039490_NX" → NXT (넥스트레이드)
"039490_AL" → 통합종목 (KRX + NXT 합산)

기존 6자리 종목코드 뒤에 _NX 또는 _AL을 붙이는 방식이에요. KRX는 기존 그대로 쓰면 됩니다. 이 규칙은 종목코드가 입력항목에 있는 TR 68개에 공통 적용돼요.

// 의사 코드 — 거래소별 조회 요청

// KRX 조회 (기존 방식 그대로)
SetInputValue("종목코드", "039490");
CommRqData(RQName, "OPT10001", 0, "화면번호");

// NXT 조회 (뒤에 _NX 추가)
SetInputValue("종목코드", "039490_NX");
CommRqData(RQName, "OPT10001", 0, "화면번호");

// 통합종목 조회 (뒤에 _AL 추가)
SetInputValue("종목코드", "039490_AL");
CommRqData(RQName, "OPT10001", 0, "화면번호");

거래소구분 파라미터 — 또 다른 분기 방식

종목코드로 거래소를 구분하는 방식 외에, "거래소구분"이라는 입력항목을 사용하는 TR도 43개 있습니다. 이 경우에는 종목코드를 바꾸는 게 아니라, 별도 파라미터로 거래소를 지정해요.

거래소구분 값
1 → KRX (한국거래소)
2 → NXT (넥스트레이드)
3 → 통합

대상 TR에는 신고저가요청(OPT10016), 거래량급증(OPT10023), 거래대금상위(OPT10032), 외인매매상위(OPT10034) 등 주로 순위/랭킹 조회 성격의 TR들이 포함돼 있어요.

// 의사 코드 — 거래소구분 파라미터 사용

// NXT 거래량급증 조회
SetInputValue("거래소구분", "2");   // 2 = NXT
SetInputValue("시장구분", "000");  // 기타 필수 입력값
CommRqData(RQName, "OPT10023", 0, "화면번호");

실시간 시세 — 조회하면 자동으로 따라온다

키움 OpenAPI에서 실시간 데이터는 별도로 요청하는 게 아니라, 조회(CommRqData)를 한 종목에 대해 자동으로 실시간 데이터가 수신됩니다. 실시간 데이터는 OnReceiveRealData 이벤트로 들어와요.

중요한 건, 실시간 데이터의 종목코드도 조회할 때 사용한 코드 그대로 전달된다는 점이에요. "039490_AL"로 조회했으면 실시간에서도 "039490_AL"이 오고, "039490_NX"로 조회했으면 "039490_NX"가 옵니다.

// 의사 코드 — 실시간 데이터 수신
void OnReceiveRealData(string sCode, string sRealType, string sRealData)
{
    // sCode → "039490_AL" 또는 "039490_NX" (조회한 코드 그대로)
    // sRealType → "주식체결", "주식호가잔량" 등

    if (sRealType == "주식체결")
    {
        현재가 = GetCommRealData(sCode, 10);   // FID 10: 현재가
        거래량 = GetCommRealData(sCode, 15);   // FID 15: 거래량
        체결시간 = GetCommRealData(sCode, 20); // FID 20: 체결시간
    }
}
주의! OpenAPI TR 목록은 요건에 따라 수시로 변경될 수 있습니다. 주기적으로 버전처리 후 KOA Studio에서 최신 TR 목록을 확인하세요.

전체 WinForms 예제 — 거래소별 조회 + 실시간 수신

위 내용을 전부 합친 WinForms 예제입니다. 종목코드를 입력하고 거래소를 선택해서 조회하면, 시세 데이터를 가져오고 실시간 체결 데이터까지 수신하는 구조예요.

using AxKHOpenAPILib;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace KiwoomMarketViewer
{
    public partial class MainForm : Form
    {
        private Button btnLogin, btnRequest;
        private TextBox txtStockCode, txtLog;
        private ComboBox cboExchange;
        private Label lblStatus;
        private int scrNo = 1000;  // 화면번호 관리용

        public MainForm()
        {
            InitializeComponent();
            CreateControls();

            axKHOpenAPI1.OnEventConnect += OnEventConnect;
            axKHOpenAPI1.OnReceiveTrData += OnReceiveTrData;
            axKHOpenAPI1.OnReceiveRealData += OnReceiveRealData;
            axKHOpenAPI1.OnReceiveMsg += OnReceiveMsg;
        }

        private void CreateControls()
        {
            btnLogin = new Button { Text = "로그인", Location = new Point(12, 12), Size = new Size(80, 30) };
            btnLogin.Click += (s, e) => axKHOpenAPI1.CommConnect();

            lblStatus = new Label { Text = "대기중...", Location = new Point(100, 18), AutoSize = true };

            txtStockCode = new TextBox { Text = "005930", Location = new Point(12, 55), Size = new Size(100, 22) };

            // 거래소 선택 콤보박스
            cboExchange = new ComboBox
            {
                Location = new Point(120, 55), Size = new Size(120, 22),
                DropDownStyle = ComboBoxStyle.DropDownList
            };
            cboExchange.Items.AddRange(new[] { "KRX (기존)", "NXT (넥스트레이드)", "통합 (AL)" });
            cboExchange.SelectedIndex = 0;

            btnRequest = new Button { Text = "시세 조회", Location = new Point(250, 53), Size = new Size(90, 26) };
            btnRequest.Click += (s, e) => RequestStockInfo();

            txtLog = new TextBox
            {
                Multiline = true, ReadOnly = true,
                Location = new Point(12, 90), Size = new Size(460, 220),
                ScrollBars = ScrollBars.Vertical
            };

            Controls.AddRange(new Control[] { btnLogin, lblStatus, txtStockCode, cboExchange, btnRequest, txtLog });
        }

        // ── 거래소별 종목코드 생성 + 조회 요청 ──
        private void RequestStockInfo()
        {
            string code = txtStockCode.Text.Trim();
            string suffix = cboExchange.SelectedIndex switch
            {
                1 => "_NX",  // NXT
                2 => "_AL",  // 통합
                _ => ""      // KRX (기존)
            };
            string fullCode = code + suffix;

            axKHOpenAPI1.SetInputValue("종목코드", fullCode);
            axKHOpenAPI1.CommRqData("주식기본정보", "opt10001", 0, (++scrNo).ToString());
            AppendLog($"[조회요청] {fullCode}");
        }

        // ── 로그인 결과 ──
        private void OnEventConnect(object sender, _DKHOpenAPIEvents_OnEventConnectEvent e)
        {
            lblStatus.Text = e.nErrCode == 0 ? "로그인 성공!" : $"로그인 실패({e.nErrCode})";
            if (e.nErrCode == 0)
                AppendLog($"[로그인] {axKHOpenAPI1.GetLoginInfo("USER_NAME")} 접속완료");
        }

        // ── 조회 데이터 수신 ──
        private void OnReceiveTrData(object sender, _DKHOpenAPIEvents_OnReceiveTrDataEvent e)
        {
            if (e.sRQName == "주식기본정보")
            {
                string name   = axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "종목명").Trim();
                string price  = axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "현재가").Trim();
                string volume = axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "거래량").Trim();
                string rate   = axKHOpenAPI1.GetCommData(e.sTrCode, e.sRQName, 0, "등락율").Trim();

                AppendLog($"[조회결과] {name} | 현재가:{price} | 거래량:{volume} | 등락율:{rate}%");
            }
        }

        // ── 실시간 데이터 수신 ──
        private void OnReceiveRealData(object sender, _DKHOpenAPIEvents_OnReceiveRealDataEvent e)
        {
            if (e.sRealType == "주식체결")
            {
                string price = axKHOpenAPI1.GetCommRealData(e.sRealKey, 10);  // 현재가
                string vol   = axKHOpenAPI1.GetCommRealData(e.sRealKey, 15);  // 거래량
                string time  = axKHOpenAPI1.GetCommRealData(e.sRealKey, 20);  // 체결시간

                AppendLog($"[실시간] {e.sRealKey} | {time} | 현재가:{price} | 거래량:{vol}");
            }
        }

        // ── 서버 메시지 ──
        private void OnReceiveMsg(object sender, _DKHOpenAPIEvents_OnReceiveMsgEvent e)
        {
            AppendLog($"[메시지] {e.sMsg}");
        }

        private void AppendLog(string msg)
        {
            txtLog.AppendText(msg + "\r\n");
        }
    }
}

핵심 로직 뜯어보기

1. 거래소별 종목코드 생성

string suffix = cboExchange.SelectedIndex switch
{
    1 => "_NX",  // NXT
    2 => "_AL",  // 통합
    _ => ""      // KRX
};
string fullCode = code + suffix;

콤보박스 선택값에 따라 접미사를 붙이는 방식이에요. KRX는 기존 6자리 그대로, NXT는 _NX, 통합은 _AL을 붙입니다. 이렇게 만든 fullCodeSetInputValue()에 넣으면 해당 거래소의 데이터가 조회됩니다.

2. 실시간 데이터 수신 — GetCommRealData와 FID

if (e.sRealType == "주식체결")
{
    string price = axKHOpenAPI1.GetCommRealData(e.sRealKey, 10);  // FID 10: 현재가
    string vol   = axKHOpenAPI1.GetCommRealData(e.sRealKey, 15);  // FID 15: 거래량
    string time  = axKHOpenAPI1.GetCommRealData(e.sRealKey, 20);  // FID 20: 체결시간
}

실시간 데이터는 조회와 다르게 GetCommRealData()로 꺼내고, 필드명 대신 FID(숫자)를 사용합니다. sRealType으로 데이터 종류를 먼저 구분한 뒤, 해당 FID로 값을 가져오는 구조예요. e.sRealKey에는 조회할 때 사용한 종목코드(접미사 포함)가 그대로 들어옵니다.

마무리하며

대체거래소 도입으로 종목코드 체계가 약간 복잡해졌지만, 핵심은 간단합니다. KRX는 기존 그대로, NXT는 _NX, 통합은 _AL만 붙이면 돼요. 조회 데이터는 OnReceiveTrData + GetCommData, 실시간 데이터는 OnReceiveRealData + GetCommRealData로 받는다는 차이점만 기억하면 됩니다!
반응형

관련글 더보기

댓글 영역