galaxis-po/docs/plans/2026-04-17-krx-openapi-migration.md
머니페니 9ab232ba12 feat: KRX Open API migration with pykrx fallback
- Add pykrx-openapi dependency
- New krx_client.py wrapper module
- ETFCollector: Open API bulk fetch + pykrx fallback
- ETFPriceCollector: Open API date-based bulk + pykrx fallback
- StockCollector: Open API base_info + daily_trade + pykrx fallback
- PriceCollector: Open API date-based bulk + pykrx fallback
- ValuationCollector: pykrx retained (Open API has no PER/PBR)
- generate_snapshots.py: Open API + pykrx fallback
- Auto-switch based on KRX_OPENAPI_KEY env var
- All 278 tests passing
2026-04-17 23:07:09 +09:00

3.9 KiB

KRX Open API 전환 설계

배경

  • pykrx는 KRX 웹 스크래핑 방식으로 불안정 (로그인 필수화, 구조 변경 시 깨짐)
  • KRX Open API (openapi.krx.co.kr) 공식 REST API로 전환하여 안정성 확보
  • pykrx-openapi 라이브러리 활용 (MIT, pip install pykrx-openapi)

전환 범위

현재 pykrx 의존 Collector (5개)

Collector pykrx 함수 KRX Open API 대체
StockCollector get_market_ticker_list, get_market_cap_by_ticker, get_market_fundamental_by_ticker get_stock_base_info, get_stock_daily_trade, get_kosdaq_stock_daily_trade
PriceCollector get_market_ohlcv get_stock_daily_trade, get_kosdaq_stock_daily_trade
ValuationCollector get_market_fundamental_by_ticker pykrx 유지 또는 별도 소스 (Open API에 PER/PBR 없음)
ETFCollector ETF_전종목기본종목().fetch() get_etf_daily_trade (종목 목록 겸용)
ETFPriceCollector get_etf_ohlcv_by_date get_etf_daily_trade

주의: ValuationCollector

KRX Open API 서비스 목록에 PER/PBR/배당수익률 API가 없음. → ValuationCollector는 pykrx(스크래핑) 유지 또는 네이버/FnGuide 등 대체 소스 검토. → 1차 전환에서는 pykrx fallback으로 유지.

스크립트 의존

  • scripts/generate_snapshots.pypykrx_stock.get_etf_ohlcv_by_date 사용
  • jobs/kjb_signal_job.pyPrice.ticker == "069500" (DB 조회, pykrx 직접 의존 없음)
  • app/services/optimizer.py — DB 조회만, pykrx 직접 의존 없음

구현 계획

1. KRX Open API 클라이언트 모듈 생성

파일: backend/app/services/krx_client.py

from pykrx_openapi import KRXOpenAPI

class KRXClient:
    """Thin wrapper around pykrx-openapi with project defaults."""
    
    def __init__(self, api_key: str = None):
        self.client = KRXOpenAPI(
            api_key=api_key or os.getenv("KRX_OPENAPI_KEY"),
            rate_limit=10,
            per_seconds=1,
            timeout=30,
        )
    
    def get_etf_daily(self, date: str) -> pd.DataFrame: ...
    def get_stock_daily(self, date: str, market: str) -> pd.DataFrame: ...
    def get_stock_base_info(self, date: str, market: str) -> pd.DataFrame: ...

2. Collector 전환 (4개)

각 collector에 KRX_OPENAPI_KEY 환경변수가 있으면 Open API 사용, 없으면 pykrx fallback.

  • ETFCollectorget_etf_daily_trade 로 전종목 ETF 목록 + 기본정보 취득
  • ETFPriceCollectorget_etf_daily_trade 로 종가/거래량 취득
  • StockCollectorget_stock_base_info + get_stock_daily_trade + get_kosdaq_stock_daily_trade
  • PriceCollectorget_stock_daily_trade + get_kosdaq_stock_daily_trade

3. ValuationCollector

1차: pykrx 유지 (KRX_ID/KRX_PW 사용) 향후: 네이버 금융 또는 FnGuide 스크래핑으로 전환 검토

4. generate_snapshots.py 전환

pykrx_stock.get_etf_ohlcv_by_dateKRXClient.get_etf_daily

5. 의존성 변경

  • pyproject.toml: pykrx-openapi 추가
  • pykrx 는 ValuationCollector 용으로 유지
  • .env.example: KRX_OPENAPI_KEY 추가
  • Gitea Secrets: KRX_OPENAPI_KEY 추가

6. 테스트

  • 기존 unit test 업데이트 (mock 대상 변경)
  • KRX Open API mock으로 테스트

환경 변수

KRX_OPENAPI_KEY=xxx     # KRX Open API 인증키 (신규)
KRX_ID=xxx              # pykrx 웹 로그인 (ValuationCollector용, 유지)
KRX_PW=xxx              # pykrx 웹 로그인 (ValuationCollector용, 유지)

롤백 전략

  • KRX_OPENAPI_KEY 환경변수를 제거하면 자동으로 pykrx fallback
  • 기존 pykrx 코드는 삭제하지 않고 fallback으로 유지

작업 완료 조건

  • KRX Open API 클라이언트 모듈
  • ETFCollector 전환
  • ETFPriceCollector 전환
  • StockCollector 전환
  • PriceCollector 전환
  • generate_snapshots.py 전환
  • 테스트 통과
  • .env.example / deploy.yml 업데이트