Post

(OMS 3) 시세 수신

OMS에서 거래소 시세를 수신하는 방법을 다룹니다. 코스콤 MDCS 가입부터 접속표준서 확인, 파생 호가 메시지 구조까지 살펴봅니다.

(OMS 3) 시세 수신

이전 글: 중요한 숫자 관리는 정수로

이 글은 매매 시스템 시리즈의 글입니다.

다음 글: 오더북이 필요한 이유

시세 수신은 매매 시스템의 첫 단계입니다. 거래소에서 보내는 호가, 체결 정보를 받아야 전략이 시작됩니다. 한국거래소 데이터를 예시로 시세를 가공하는 과정을 살펴보겠습니다.


코스콤 MDCS (Market Data Center Services)

한국거래소 시세 전문 (메시지) 프로토콜은 다음 사이트에서 확인할 수 있습니다. 회원 가입을 하면 전문 스펙이 바뀔 때마다 메일도 보내 줍니다.

https://data.koscom.co.kr

먼저 회원 가입을 하고, 로그인하면 접속표준서를 다운받을 수 있습니다

접속표준서 다운로드

메뉴에서 접속표준서 → 실시간시장정보로 이동해서 최신 파일을 다운받습니다. 현재 시점 (2026-01-17) 최신 파일은 다음과 같습니다.

1
접속표준서(정보분배-UDP_TCP실시간)_v2.010-배포용.xlsx

이 엑셀 파일에 시세 메시지의 포맷이 정의되어 있고, 메시지 종류별로 시트가 나뉘어 있습니다.


예시: 파생 호가 메시지 구조

예시로 파생상품(선물, 옵션)의 우선호가 5단계 메시지 구조를 한번 살펴볼까요? 인터페이스정의서에서 볼 수 있습니다:

필드명설명타입길이누적
Data Category데이터 구분String22
Information Category정보 구분String35
Message sequence number메시지 일련번호Int813
Board ID보드 IDString215
Session ID세션 IDString217
ISIN Code종목코드String1229
A designated number for an issue종목 지정번호Int635
Processing Time of Trading System처리 시각String1247
Ask Level 1 price매도 1호가Double956
Bid Level 1 price매수 1호가Double965
Ask Level 1 volume매도 1잔량Int974
Bid Level 1 volume매수 1잔량Int983
Ask Level 1_Order Counts매도 1건수Int588
Bid Level 1_Order Counts매수 1건수Int593
Ask Level 2 price매도 2호가Double9102
Bid Level 2 price매수 2호가Double9111
Ask Level 2 volume매도 2잔량Int9120
Bid Level 2 volume매수 2잔량Int9129
Ask Level 2_Order Counts매도 2건수Int5134
Bid Level 2_Order Counts매수 2건수Int5139
Ask Level 3 price매도 3호가Double9148
Bid Level 3 price매수 3호가Double9157
Ask Level 3 volume매도 3잔량Int9166
Bid Level 3 volume매수 3잔량Int9175
Ask Level 3_Order Counts매도 3건수Int5180
Bid Level 3_Order Counts매수 3건수Int5185
Ask Level 4 price매도 4호가Double9194
Bid Level 4 price매수 4호가Double9203
Ask Level 4 volume매도 4잔량Int9212
Bid Level 4 volume매수 4잔량Int9221
Ask Level 4_Order Counts매도 4건수Int5226
Bid Level 4_Order Counts매수 4건수Int5231
Ask Level 5 price매도 5호가Double9240
Bid Level 5 price매수 5호가Double9249
Ask Level 5 volume매도 5잔량Int9258
Bid Level 5 volume매수 5잔량Int9267
Ask Level 5_Order Counts매도 5건수Int5272
Bid Level 5_Order Counts매수 5건수Int5277
Open Step Ask Total Volume시가 매도 총잔량Int9286
Open Step Bid Total Volume시가 매수 총잔량Int9295
Open Step Ask Total Counts시가 매도 총건수Int5300
Open Step Bid Total Counts시가 매수 총건수Int5305
Estimated Trading Price예상 체결가Double9314
Estimated Trading Volume예상 체결수량Int9323
End Keyword종료 문자String1324

전체 메시지 길이는 324바이트입니다. 실제 메시지가 어떻게 생겼는지 보겠습니다:

1
B606F00001138G140KR4167VC0005013381075501053697000117.28000117.270000000020000000090000100004000117.29000117.260000000560000000040000700003000117.30000117.250000001170000000140000600003000117.31000117.240000001290000000120001100002000117.32000117.2300000004500000011700010000050000014840000014390042500400000000.00000000000

인터페이스 정의서에 따르면 주요 필드들의 내용은 이렇습니다:

필드오프셋길이추출값해석
ISIN Code1712KR4167VC000510년 국채 선물 (종목 코드 167)
Processing Time351207550105369707:55:01.053697
Ask Level 1 price479000117.28117.28
Bid Level 1 price569000117.27117.27
Ask Level 1 volume6590000000022계약
Bid Level 1 volume7490000000099계약

고정 길이 텍스트 포맷

눈여겨볼 점은 KRX 데이터의 모든 필드가 고정 길이 텍스트라는 점입니다. 소수점 위치도 정해져 있습니다. 참고-가격표시정보 시트를 보면 상품별 소수점 자리수가 정의되어 있습니다.

이전 글에서 정수 관리의 중요성을 다뤘죠. 그럼 수신한 가격을 먼저 정수로 파싱할 건데, 고정 자릿수라는 점을 이용해 최적화가 가능합니다.

제가 선호하는 방식은 정수 부분 전체를 한 칸 옮겨서 소수점을 없앤 뒤 정수로 변환하는 겁니다. (int)(100.05 * 100) 같은 곱셈과 캐스팅 방식은 부동소수점 오차가 생길 수 있어서 선호하지 않습니다

1
2
3
4
5
// pseudocode
input = "000100.05"
memcpy(buf,     input,     6)  // "000100"
memcpy(buf + 6, input + 7, 2)  // "05"
result = parse_int(buf)        // 10005 (원래 100.05를 소수점 2자리 기준 정수로 표현)

실제 코드는 상품별 소수점 자리수 처리, 버퍼 관리 등이 추가되어 조금 더 복잡합니다.

다른 데이터 소스처럼 소수점 위치가 고정되어 있지 않은 경우에는 정수부와 소수부를 따로 파싱해서 처리합니다:

1
2
3
4
5
6
7
8
9
10
// pseudocode
input = "123.45"

dot_pos = find('.')
integer_part = parse_int(input[0..dot_pos])      // 123
decimal_part = parse_int(input[dot_pos+1..end])  // 45
decimal_digits = 2

scale = 10^(target_decimals - decimal_digits)    // 10^0 = 1
result = integer_part * 10^target_decimals + decimal_part * scale  // 12345

가격 파싱 오류는 잘못된 주문으로 이어질 수 있으니까 테스트 코드를 충분히 작성해두는 게 중요합니다.


관련 글


다음 글에서는

다음 글에서는 오더북이 왜 필요한지 살펴봅니다.

This post is licensed under CC BY 4.0 by the author.