- 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
68 lines
2.3 KiB
Python
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
|