galaxis-po/backend/app/services/krx_client.py
머니페니 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

68 lines
2.3 KiB
Python

"""
KRX Open API client wrapper.
Uses pykrx-openapi library to access KRX official REST API.
Activated when KRX_OPENAPI_KEY environment variable is set.
"""
import os
import logging
from datetime import datetime
import pandas as pd
from pykrx_openapi import KRXOpenAPI
logger = logging.getLogger(__name__)
class KRXClient:
"""Thin wrapper around pykrx-openapi with project defaults."""
def __init__(self, api_key: str | None = None):
key = api_key or os.getenv("KRX_OPENAPI_KEY")
if not key:
raise ValueError("KRX_OPENAPI_KEY is required for KRXClient")
self.client = KRXOpenAPI(api_key=key)
def _to_date_str(self, date_input: str) -> str:
"""Ensure date string is in YYYYMMDD format."""
return date_input.replace("-", "")
def _records_to_df(self, result: dict) -> pd.DataFrame:
"""Convert API response dict to DataFrame."""
records = result.get("OutBlock_1", [])
if not records:
return pd.DataFrame()
return pd.DataFrame(records)
def get_etf_daily(self, bas_dd: str) -> pd.DataFrame:
"""Fetch all ETF daily trade data for a given date."""
bas_dd = self._to_date_str(bas_dd)
result = self.client.get_etf_daily_trade(bas_dd=bas_dd)
return self._records_to_df(result)
def get_stock_daily(self, bas_dd: str, market: str = "KOSPI") -> pd.DataFrame:
"""Fetch stock daily trade data for a given date and market."""
bas_dd = self._to_date_str(bas_dd)
if market == "KOSDAQ":
result = self.client.get_kosdaq_stock_daily_trade(bas_dd=bas_dd)
else:
result = self.client.get_stock_daily_trade(bas_dd=bas_dd)
return self._records_to_df(result)
def get_stock_base_info(self, bas_dd: str, market: str = "KOSPI") -> pd.DataFrame:
"""Fetch stock base info for a given date and market."""
bas_dd = self._to_date_str(bas_dd)
if market == "KOSDAQ":
result = self.client.get_kosdaq_stock_base_info(bas_dd=bas_dd)
else:
result = self.client.get_stock_base_info(bas_dd=bas_dd)
return self._records_to_df(result)
def get_krx_client() -> KRXClient | None:
"""Return KRXClient if KRX_OPENAPI_KEY is set, else None."""
key = os.getenv("KRX_OPENAPI_KEY")
if key:
return KRXClient(api_key=key)
return None