수정주가여야 한다: 코드 주석이 지키지 못한 약속

2026-07-31

어느 날 코스피 30일 하락률 1위에 미래산업(025560)이 -76.0%로 올라왔습니다. 한 달 만에 4분의 3이 증발한 종목. 그런데 같은 화면 옆에 붙은 뉴스가 이상했습니다. "미래산업, 액면분할 거래 재개 첫날 26% 급등." 폭락한 종목의 뉴스가 급등이라니, 앞뒤가 맞지 않았습니다.

답은 간단했습니다. 미래산업은 액면가를 500원에서 100원으로 나누는 5대 1 액면분할을 했고, 거래가 재개된 날이 마침 우리 조회 기준일이었습니다. 주식 수가 5배로 늘고 주가는 5분의 1이 됐을 뿐, 회사의 가치가 줄어든 게 아닙니다. -76%는 실제 하락이 아니라, 우리 데이터가 분할을 반영하지 못해 생긴 착시였습니다.

"수정주가여야 한다"

우리 가격 테이블에는 조정 종가를 담는 컬럼이 하나 있습니다. 이름 그대로 '수정주가(adjusted close)', 즉 액면분할·병합 같은 사건을 소급 반영해서 시점 간 비교가 가능하도록 조정한 종가입니다. 코드에는 주석까지 달려 있었습니다. 이 값은 반드시 수정주가여야 하며, 분할이 반영되지 않으면 장기 수익률 계산 전체가 오염된다는 취지의 경고였습니다.

문제는, 주석은 약속일 뿐 강제가 아니라는 것입니다. 컬럼에 "수정주가여야 한다"고 적어 둔다고 해서, 저장되는 값이 실제로 수정주가가 되지는 않습니다. 그리고 미래산업의 경우, 그 약속은 지켜지지 않고 있었습니다.

한 컬럼에 섞인 두 개의 기준

데이터를 열어 보니 이랬습니다. 분할 전 마지막 실제 거래일(2026-07-06)의 종가는 41,750원. 거래정지가 풀리고 매매가 재개된 날(2026-07-27)의 종가는 10,210원. 그 사이 거래정지 기간에 기준가가 41,750원에서 8,350원으로 정확히 5배 낮아져 있었습니다 — 거래는 한 건도 없이, 거래소가 신주 기준으로 기준가만 재산정한 것입니다.

즉 같은 컬럼 안에서, 분할 이전 행들은 여전히 구주 기준(예: 42,600원)으로 남아 있고, 분할 이후 행만 신주 기준(8,350원, 10,210원)이었습니다. 하나의 컬럼에 서로 다른 두 개의 기준이 섞여 있었던 겁니다. 그리고 과거 행을 신주 기준으로 소급해 맞춰 주는 로직은 어디에도 없었습니다.

-76%의 정체는 이 혼재였습니다. 30일 전 구주 기준 42,600원과 오늘 신주 기준 10,210원을 그대로 나누면 -76%가 나옵니다. 5분의 1로 쪼갠 것을 하락으로 읽은 셈입니다. 기준을 통일해 다시 계산하니 같은 기간 수익률은 +19.84%였습니다. 하락 1위가 아니라, 그냥 오른 종목이었습니다.

미래산업(025560) — 정정 전후 (기준일 2026-07-27, 30일)
항목정정 전정정 후
2026-06-26 종가42,600원 (구주 기준)8,520원 (신주 기준)
2026-07-27 종가10,210원10,210원
30일 수익률-76.03%+19.84%

왜 아무도 안 고쳤나

가장 불편한 부분은 이겁니다. 이건 어려운 버그가 아닙니다. 액면분할 처리는 금융 데이터의 기본이고, 수정주가라는 개념 자체가 바로 이걸 위해 존재합니다. 그런데도 왜 놓쳤을까요.

일일 수집 배치는 매일 최근 며칠 치만 다시 받아옵니다. 어제오늘 데이터를 갱신하는 데는 충분하지만, 분할이 일어났을 때 몇 년 치 과거 가격을 통째로 다시 조정하는 일은 하지 않습니다. 분할은 자주 있는 일이 아니고, "오래된 데이터는 건드리지 않는다"는 규칙은 평소엔 합리적이니까요. 그 합리적인 규칙이, 분할이라는 드문 사건 앞에서 조용히 데이터를 오염시켰습니다. 무해해 보이는 이름의 규칙이 가장 위험할 때가 있습니다.

컬럼에 "수정주가여야 한다"고 적는 것과, 매일 그 값이 실제로 수정주가인지 확인하는 것은 다른 일입니다.

미래산업 하나가 아니었다

한 종목을 고치고 나니, 같은 증상을 가진 종목이 더 있었습니다. 어떤 건 분할이라 가격이 낮아져 있었고, 어떤 건 반대로 병합이라 가격이 부풀어 있었습니다. 심지어 병합 직후에 유상증자가 겹쳐, 단순한 배수 하나로는 정정이 불가능한 종목도 있었습니다. 정정할 수 있는 건 소급 조정하고, 정정이 불가능한 건 랭킹에서 제외했습니다. 지켜지지 않은 약속 하나가 이렇게 여러 갈래로 번져 있었습니다.

같은 버그, 다른 얼굴 — 나스닥

흥미로운 건, 같은 종류의 오염이 나스닥에서는 전혀 다른 모습으로 나타났다는 점입니다. 나스닥 5년 하락률 상위에 -107.3%라는 값이 있었습니다. 수익률은 아무리 떨어져도 -100%가 한계입니다. 주가가 0이 되면 -100%, 그 아래는 수학적으로 불가능합니다. -107%는 계산이 어딘가에서 하한을 뚫었다는 뜻이었습니다.

원인은 코스피와 달랐습니다. 나스닥 데이터 소스는 과거 배당·분배를 소급 조정하는데, 누적 분배액이 그 시점의 과거 주가보다 커지면 조정된 가격이 음수로 내려갑니다. 음수 주가로 수익률을 계산하니 -100% 하한이 깨진 것이죠. 같은 "수정주가 오염"이지만, 코스피에서는 기준 혼재로, 나스닥에서는 음수 가격으로 나타난 겁니다. 데이터 소스가 다르면 같은 종류의 실수도 다른 가면을 씁니다.

참고로 그 옆에 있던 -100.0%는 버그가 아니었습니다. 실제로는 -99.998%였고, 극단적으로 폭락한 진짜 종목이었습니다. 딱 떨어지는 -100%가 오히려 사람을 의심하게 만들었지만, 이건 데이터가 정직했던 경우입니다. 진짜 폭락과 계산 오류를 구분하는 일은, 둘 다 -100% 근처에 있을 때 특히 까다롭습니다.

문서도 틀려 있었다

정리하는 김에 하나 더 발견했습니다. 우리 방법론 문서는 나스닥 수익률이 배당을 반영한 총수익 기준이라고 설명하고 있었는데, 위 수정 과정에서 나스닥을 분할만 반영하고 배당은 제외하는 방식으로 바꾸면서, 그 설명이 사실과 어긋나게 됐습니다. 문서와 데이터가 서로 다른 말을 하는 것 — 이것도 조용한 오류의 일종입니다. 눈에 잘 안 띄지만, 읽는 사람을 틀린 곳으로 안내하니까요. 문서도 실제에 맞게 고쳤습니다. (두 데이터 소스의 배당 처리 차이가 애초에 어떻게 문제가 되는지는 같은 이름의 다른 수정주가에서 따로 다뤘습니다.)

남기는 것

FomoBot은 이미 지나간 가격을 정리해 보여주는 도구입니다. 그 가격이 틀리면 나머지는 전부 의미가 없습니다. 이번 일에서 우리가 배운 건 하나입니다 — 계약은 강제되지 않으면 조용히 깨진다. 파이프라인이 이상하면 전략을 의심하기 전에 데이터를 먼저 의심해야 한다는, 전에도 겪은 교훈과 같은 계열입니다.

지금은 분할·병합·음수 가격을 정기적으로 감시하는 장치를 붙여 두었습니다. 다음에 또 같은 계약이 깨지면, 이번처럼 우연히 뉴스를 보고 알아채는 게 아니라, 시스템이 먼저 알려 줄 겁니다. 우리는 틀렸고, 고쳤습니다. 그리고 무엇을 어떻게 틀렸는지 여기 적어 둡니다 — 그게 다음 사람이(그게 미래의 우리라도) 같은 자리에서 덜 헤매는 유일한 방법이니까요.

데이터 기준 — 본문의 수치는 해당 사건일(2026년 7월) 전후의 실제 데이터이며, 정정 전 값과 정정 후 값을 함께 표기했습니다. 수익률에는 거래비용과 세금이 반영되지 않았습니다.

생존 편향 — 상장폐지된 종목은 데이터에 포함되지 않으므로, 랭킹의 수치들은 실제 시장보다 낙관적입니다. 자세한 내용은 생존 편향 안내를 참고하십시오.

정정·갱신 정책 — 이 글에서 다룬 종목들의 가격 데이터는 정정을 완료했으며, 이후 발생하는 분할·병합·음수 가격은 정기 감시로 탐지합니다. 정정이 불가능한 종목은 랭킹에서 제외합니다.

투자 판단에 대하여 — FomoBot은 이미 지나간 가격을 정리해 보여주는 도구입니다. 특정 종목의 매수·매도를 권유하지 않으며, 이 글의 어떤 내용도 투자 자문이 아닙니다. 투자 손익의 책임은 투자자 본인에게 있습니다.