KRX 수급 데이터 연동 PRD
배경 (Background)
현재 주식앱은 토스증권 Open API 기반으로 가격·호가·체결·캔들만 확보한다.
토스 Open API 전체 명세(Auth / Market Data / Stock Info / Market Info / Account·Asset / Order 6개 카테고리)를 확인한 결과, 투자자별 매매 동향(외국인·기관 수급) 엔드포인트가 없다 . investor / foreign / institution path가 존재하지 않는다.
추천 종목 산정은 현재 기술 모멘텀·뉴스 감성 중심이다. 외국인·기관 수급, 밸류에이션, 공매도·신용 같은 시장 미시구조 데이터 가 빠져 있어 중기 시그널의 근거가 약하다.
이 데이터는 KRX 정보데이터시스템(data.krx.co.kr, MDC)에서 EOD(장 마감 기준)로 제공된다.
목표
KRX에서 외국인·기관 수급, 외국인 보유율, 밸류에이션, 공매도, 신용잔고를 전종목·일 단위로 수집·영속화 한다.
수집 완료를 Kafka 이벤트로 발행 하고, worker 서버 가 이를 소비해 추천·포트폴리오·관심종목 도메인의 예측 데이터를 갱신 한다.
예측 연산은 ml(stateless)이 수행하고, 영속화는 BE가 소유한다. worker는 오케스트레이션만 담당한다.
KRX 스크래핑의 사이드 이펙트(차단·잠정치 노이즈·약관 리스크)를 통제한다.
요구사항
ID 요구사항 우선순위 R-01 투자자별 거래실적(개인·외국인·기관 종목별 순매수)을 일 단위로 수집·저장한다 P0 R-02 외국인 보유량·보유율·한도소진율을 일 단위로 수집·저장한다 P0 R-03 전종목 PER·PBR·배당수익률을 일 단위로 수집·저장한다 P1 R-04 공매도 거래·잔고를 일 단위로 수집·저장한다 (공시 시차 반영) P1 R-05 신용거래 잔고를 일 단위로 수집·저장한다 P1 R-06 장 마감 후 1일 1회 배치로 전종목을 수집한다 (실시간 수집 미수행) P0 R-06b 필요 타이밍에 수동으로 수집을 트리거하는 갱신 API를 제공한다 (멱등) P0 R-07 KRX 호출은 OTP 토큰 발급(bld→getJsonData) 구조를 따르고, 재시도·백오프·rate 제어로 차단을 회피한다 P0 R-08 종목·기준일별 수급 팩터를 조회하는 read API를 제공한다 (worker 재계산 입력용) P0 R-09 ml 예측 연산이 KRX 팩터를 입력으로 받아 스코어에 반영한다 P1 R-10 일부 KRX 통계 수집이 실패해도 나머지는 적재한다 (부분 실패 허용) P1 R-11 배치 실행 결과(run별·통계별 상태·행수·소요·에러)를 영속 기록하고, 실패·부분 실패 시 Discord로 알린다 P0 R-12 수집 행수·소요·재시도 등 메트릭을 계측해, 청크 기반 배치로 전환할 임계 시그널을 관측한다 P1 R-13 수집 완료 시 marketflow.collected.v1(기준일 포함) 이벤트를 Kafka로 발행한다 P0 R-14 worker 서버가 이벤트를 소비해 추천·포트폴리오·관심종목 예측 재계산을 오케스트레이션한다 P0 R-15 재계산 결과(예측 데이터)를 기준일 기준으로 멱등하게 영속화한다 (동일 이벤트 재처리 안전) P0 R-16 worker 소비 실패는 재시도 후 DLQ로 격리하고, 소비 지연·DLQ를 관측한다 P1
사용자 시나리오
시나리오 1: 마감 후 수급 적재
장 마감 후 배치 스케줄러가 기동한다.
KRX에서 투자자별 거래실적·외국인 보유·밸류에이션·공매도·신용을 순차 수집한다.
전종목 데이터를 기준일과 함께 MySQL에 upsert한다.
일부 통계 수집이 실패하면 해당 항목만 건너뛰고 나머지를 적재한다.
시나리오 2: 수동 갱신
확정치 게시 지연·데이터 누락·당일 재수집이 필요한 상황에서 운영자가 수동 갱신 API를 호출한다.
자동 스케줄러와 동일한 수집 로직이 실행된다.
기준일(baseDate)을 지정하거나 미지정 시 직전 영업일로 수집한다.
멱등 upsert라 동일 기준일 재호출이 안전하다.
시나리오 3: 이벤트 기반 예측 갱신
수집 완료 후 BE가 marketflow.collected.v1(기준일) 이벤트를 발행한다.
worker 서버가 이벤트를 소비해 추천·포트폴리오·관심종목 재계산을 시작한다.
worker가 BE에서 입력(수급 팩터·보유종목·관심종목)을 읽어 ml에 재계산을 요청한다.
ml은 KRX 팩터를 가중에 반영해 예측을 계산한다. 팩터가 없으면 해당 가중을 제외하고 graceful하게 산정한다.
worker가 결과를 기준일 기준으로 BE에 멱등 영속화한다.
소비 실패는 재시도 후 DLQ로 격리한다. 동일 이벤트 재처리는 멱등하게 처리된다.
세부 정책
정책 1: 수집 주기 — 마감 후 1일 1배치
KRX 데이터는 EOD 확정치다. 장중 잠정치는 다음 날 확정치와 어긋난다.
실시간 폴링은 새 정보가 없고 차단·노이즈 리스크만 크므로 수행하지 않는다.
투자자별 거래실적 확정치 게시(보통 18~19시 KST) 이후 배치를 실행한다.
정책 2: 공매도 공시 시차
공매도 거래는 T+1, 공매도 잔고는 T+2에 공시된다.
저장 시 거래일(기준일) 과 공시일 을 구분해, 수급 팩터 조회 시 거래일 기준으로 정렬한다.
정책 3: 부분 실패 허용
5종 통계 중 일부 수집이 실패해도 트랜잭션을 통째로 롤백하지 않는다.
통계 단위로 적재하고, 실패 항목은 로그로 남긴다.
정책 4: 사이드 이펙트 통제
호출 간 간격·재시도·백오프를 두고, 1배치로 끝낸다.
KRX 스크래핑은 약관 회색지대임을 인지하고 고빈도 호출을 금지한다.
정책 5: 모니터링과 청크 전환 시그널
배치 run마다 통계별 상태·행수·소요·에러를 marketflow_collect_log에 남긴다 (관측 스택과 무관하게 동작).
실패·부분 실패 시 Discord로 즉시 알린다 (마감 후 배치라 사람이 실시간으로 못 봄).
수집 행수·소요·heap·재시도를 메트릭으로 계측해 Grafana 대시보드/알람으로 추세를 관측한다.
임계(단일 통계 5만 행, 전체 5분, 단일 통계 2분, 벌크 upsert 30초, heap 압박, 잦은 부분 실패/429) 지속 초과는 Spring Batch(청크·restart·partition) 전환 신호 로 본다.
정책 6: 이벤트 전파와 예측 재계산
수집 트랜잭션 커밋 후 marketflow.collected.v1(기준일)을 발행한다. 발행 실패는 로그·알림으로 남기고 수동 재트리거로 복구한다 (1일 1회·멱등이라 outbox는 도입하지 않음, 향후 재검토).
이벤트 단위는 배치당 1건 이다. 재계산은 종목별로 격리되지 않으므로(포트폴리오는 다종목 혼합) 종목별 이벤트를 쓰지 않는다.
worker는 상태를 갖지 않는다. 입력은 BE에서 읽고, 계산은 ml에 위임하며, 결과는 BE에 영속화한다.
재계산은 기준일 기준 멱등 — 동일 이벤트 재처리·수동 재수집 시 결과가 덮어쓰기된다.
소비 실패는 재시도 후 DLQ로 격리하고, 소비 지연(consumer lag)·DLQ 적재를 관측한다.
범위 외
실시간 수급 수집 (정책 1)
FE 화면 표시 — 본 과제는 수집·적재·이벤트·예측 갱신까지. UI 노출은 별도 과제.
KRX 공식 유료 데이터벤더 연동 (스크래핑으로 시작, 차단·약관 이슈 발생 시 재검토)
프로그램매매·대차잔고 (1차 스코프 제외, 효과 확인 후 확장)
트랜잭션 아웃박스(outbox) — 1일 1회·멱등·수동 재트리거로 충분, 향후 재검토
종목별 이벤트·실시간 스트리밍 재계산 (배치당 1건으로 충분)
관련 문서