Signalio — 한국 시장 정보 지도와 전략 검증 엔진 2026.09.20

하룻밤에 165MB — 검증 엔진의 바닥을 깔았다

전종목 10년 일봉 648만 행, DART 재무 63만 행, 폐지종목 417개. 데이터보다 중요한 건 같이 만든 검증 방법론 9원칙이다.

검증을 하려면 데이터가 먼저다. 하룻밤에 165MB를 모았고, 세션이 끝나도 남도록 프로젝트 안에 뒀다.

모은 것

research/
  scripts/   20개 — 수집·백테스트 스크립트
  data/      16개, 165MB
  reports/   19개 — 검증 리포트
파일내용
daily10y.csv.gz전종목 10년 일봉 2015-01~2026-09, 2,869종목, 648만 행
dart_fin.csv.gzDART 연간재무 2015~2025 — 63만 행 / 3,088사
delisted_prices.csv.gz폐지종목 일봉 417종목 / 54.7만 행
etf_prices.csv.gz국내 ETF 60종 10년 일봉
minutes.csv / minutes2.csv1분봉 (09:00~09:35 / 09:00~15:30)
index_long.csv.gz지수 36년
macro.csv.gz매크로 10종

임시 폴더에 두지 않은 이유가 있다. 앞선 세션에서 수집 스크립트를 scratchpad에 만들었다가 세션이 끝나면서 통째로 날렸다. 다시 만드는 데 한나절이 걸린다.

헛디디기 쉬운 것들 (실측으로 확인)

pykrx는 죽었다. 전종목·티커목록·fundamental API가 전부 실패한다. KRX가 로그인 정보를 요구하도록 바꿨다. 개별 종목 일봉만 된다.

StockListing('KRX-DELISTING')은 0건을 준다. 그런데 DataReader(폐지코드)로 개별 조회하면 작동한다. 450개 시도해서 417개를 받았다. "폐지종목은 못 받는다"고 적어뒀던 기존 기록이 틀렸던 것이다.

DART는 계정명이 함정이다. 당기순이익이 아니라 당기순이익(손실)이다. 영업이익과 영업이익(손실)도 둘 다 존재한다. 하나만 매핑하면 순이익이 통째로 빈다.

그리고 rcept_no 앞 8자리가 실제 접수일이다. 이걸 쓰면 "재무제표는 3월에 공시된다고 가정" 같은 어림짐작이 필요 없다. 다중회사 조회는 최대 100개/호출이고 200개를 넣으면 status=021로 거절한다. 3,990사 × 11년이 440호출, 7분이었다.

검증 방법론 9원칙

데이터보다 이게 더 중요하다. 성급한 보고로 몇 번 무너진 뒤에 고정한 규칙이다.

  1. 벤치마크 대비로만 판정한다. 절대수익 +10%도 같은 기간 지수가 +15%면 진 것이다
  2. 기간을 나눈다. 구축(~2020) / 검증(2021~). 한쪽만 좋으면 과적합
  3. 가중 방식을 맞춘다. 지수가 시총가중이면 전략도 시총가중으로 비교한다
  4. 체결 가능성을 먼저 본다. 상한가 종가 매수, 틱 단위 수익 같은 것
  5. look-ahead를 의심한다. 진입가가 종가면 청산 판정은 다음 봉부터
  6. 좋은 결과일수록 교차검증 후 보고한다
  7. 생존편향·비용·슬리피지를 매번 명시한다
  8. 시총가중 포트폴리오는 유효종목수(1/Σw²)를 확인한다
  9. 결과가 상식에 반하면 코드부터 의심한다

8번과 9번은 같은 날 각각 실제 버그를 잡아낸 조항이다. 그래서 목록에 올렸다.

이게 자산인 이유

전략은 기각되면 사라지지만, 데이터와 방법론은 다음 가설에 그대로 재사용된다. 팩터를 새로 시험할 때 value_factor.py에 지표 컬럼 하나만 추가하면 되고, 집중도 확인은 value_diagnose.py가 대신해준다.

같은 길을 두 번 파지 않기 위한 장치이기도 하다.

Signalio — 한국 시장 정보 지도와 전략 검증 엔진 프로젝트로