fix: add KR_FALLBACK_STOCKS for domestic scanner when ranking API returns empty (#153) #154
@@ -75,6 +75,14 @@ class Settings(BaseSettings):
|
|||||||
# Market selection (comma-separated market codes)
|
# Market selection (comma-separated market codes)
|
||||||
ENABLED_MARKETS: str = "KR,US"
|
ENABLED_MARKETS: str = "KR,US"
|
||||||
|
|
||||||
|
# Fallback stock list for KR domestic market when ranking API returns empty
|
||||||
|
# (KIS VTS does not return data from volume-rank API).
|
||||||
|
# Comma-separated 6-digit stock codes. Override in .env if needed.
|
||||||
|
KR_FALLBACK_STOCKS: str = (
|
||||||
|
"005930,000660,035420,005380,068270," # 삼성전자,SK하이닉스,NAVER,현대차,셀트리온
|
||||||
|
"051910,035720,006400,207940,000270" # LG화학,카카오,삼성SDI,삼성바이오로직스,기아
|
||||||
|
)
|
||||||
|
|
||||||
# Backup and Disaster Recovery (optional)
|
# Backup and Disaster Recovery (optional)
|
||||||
BACKUP_ENABLED: bool = True
|
BACKUP_ENABLED: bool = True
|
||||||
BACKUP_DIR: str = "data/backups"
|
BACKUP_DIR: str = "data/backups"
|
||||||
|
|||||||
@@ -1698,7 +1698,14 @@ async def run(settings: Settings) -> None:
|
|||||||
logger.info("Smart Scanner: Scanning %s market", market.name)
|
logger.info("Smart Scanner: Scanning %s market", market.name)
|
||||||
|
|
||||||
fallback_stocks: list[str] | None = None
|
fallback_stocks: list[str] | None = None
|
||||||
if not market.is_domestic:
|
if market.is_domestic:
|
||||||
|
# KIS VTS ranking API often returns empty for domestic.
|
||||||
|
# Use configured fallback so scanner can still run.
|
||||||
|
raw = settings.KR_FALLBACK_STOCKS if settings else ""
|
||||||
|
fallback_stocks = [
|
||||||
|
c.strip() for c in raw.split(",") if c.strip()
|
||||||
|
] or None
|
||||||
|
else:
|
||||||
fallback_stocks = await build_overseas_symbol_universe(
|
fallback_stocks = await build_overseas_symbol_universe(
|
||||||
db_conn=db_conn,
|
db_conn=db_conn,
|
||||||
overseas_broker=overseas_broker,
|
overseas_broker=overseas_broker,
|
||||||
|
|||||||
@@ -1678,3 +1678,43 @@ def test_start_dashboard_server_enabled_starts_thread() -> None:
|
|||||||
assert thread == mock_thread
|
assert thread == mock_thread
|
||||||
mock_thread_cls.assert_called_once()
|
mock_thread_cls.assert_called_once()
|
||||||
mock_thread.start.assert_called_once()
|
mock_thread.start.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# KR fallback stocks config (issue #153)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class TestKrFallbackStocksConfig:
|
||||||
|
"""Test KR_FALLBACK_STOCKS default value and parsing."""
|
||||||
|
|
||||||
|
def test_default_contains_samsung(self) -> None:
|
||||||
|
settings = Settings(
|
||||||
|
KIS_APP_KEY="k",
|
||||||
|
KIS_APP_SECRET="s",
|
||||||
|
KIS_ACCOUNT_NO="12345678-01",
|
||||||
|
GEMINI_API_KEY="g",
|
||||||
|
)
|
||||||
|
codes = [c.strip() for c in settings.KR_FALLBACK_STOCKS.split(",") if c.strip()]
|
||||||
|
assert "005930" in codes # 삼성전자
|
||||||
|
|
||||||
|
def test_default_has_ten_codes(self) -> None:
|
||||||
|
settings = Settings(
|
||||||
|
KIS_APP_KEY="k",
|
||||||
|
KIS_APP_SECRET="s",
|
||||||
|
KIS_ACCOUNT_NO="12345678-01",
|
||||||
|
GEMINI_API_KEY="g",
|
||||||
|
)
|
||||||
|
codes = [c.strip() for c in settings.KR_FALLBACK_STOCKS.split(",") if c.strip()]
|
||||||
|
assert len(codes) == 10
|
||||||
|
|
||||||
|
def test_env_override(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
monkeypatch.setenv("KR_FALLBACK_STOCKS", "005930,000660")
|
||||||
|
settings = Settings(
|
||||||
|
KIS_APP_KEY="k",
|
||||||
|
KIS_APP_SECRET="s",
|
||||||
|
KIS_ACCOUNT_NO="12345678-01",
|
||||||
|
GEMINI_API_KEY="g",
|
||||||
|
)
|
||||||
|
codes = [c.strip() for c in settings.KR_FALLBACK_STOCKS.split(",") if c.strip()]
|
||||||
|
assert codes == ["005930", "000660"]
|
||||||
|
|||||||
Reference in New Issue
Block a user