- 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
3.9 KiB
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.py—pykrx_stock.get_etf_ohlcv_by_date사용jobs/kjb_signal_job.py—Price.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.
- ETFCollector →
get_etf_daily_trade로 전종목 ETF 목록 + 기본정보 취득 - ETFPriceCollector →
get_etf_daily_trade로 종가/거래량 취득 - StockCollector →
get_stock_base_info+get_stock_daily_trade+get_kosdaq_stock_daily_trade - PriceCollector →
get_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_date → KRXClient.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 업데이트