from __future__ import annotations import os from pathlib import Path from typing import Any import yaml from pydantic import Field from pydantic_settings import BaseSettings class KISConfig(BaseSettings): app_key: str = Field(default="", alias="KIS_APP_KEY") app_secret: str = Field(default="", alias="KIS_APP_SECRET") account_no: str = Field(default="", alias="KIS_ACCOUNT_NO") account_code: str = Field(default="01", alias="KIS_ACCOUNT_CODE") hts_id: str = Field(default="", alias="KIS_HTS_ID") server_mode: str = Field(default="vps", alias="KIS_SERVER_MODE") model_config = {"env_file": ".env", "extra": "ignore"} class AppConfig(BaseSettings): host: str = Field(default="0.0.0.0", alias="APP_HOST") port: int = Field(default=8000, alias="APP_PORT") debug: bool = Field(default=False, alias="APP_DEBUG") db_path: str = Field(default="./data/stock.db", alias="DB_PATH") log_level: str = Field(default="INFO", alias="LOG_LEVEL") model_config = {"env_file": ".env", "extra": "ignore"} class CollectorConfig: interval_seconds: int = 5 market_open_hour: int = 9 market_close_hour: int = 15 market_close_minute: int = 30 class StrategyConfig: check_interval_seconds: int = 3 max_daily_trades: int = 50 default_order_type: str = "00" class TradingConfig: max_order_amount: int = 10_000_000 min_order_amount: int = 100_000 slippage_percent: float = 0.1 class RateLimitConfig: requests_per_second: float = 1.0 retry_delay: float = 1.5 class Settings: def __init__(self) -> None: self.kis = KISConfig() self.app = AppConfig() self.collector = CollectorConfig() self.strategy = StrategyConfig() self.trading = TradingConfig() self.rate_limit = RateLimitConfig() self._load_yaml() def _load_yaml(self) -> None: yaml_path = Path("config.yaml") if not yaml_path.exists(): return with open(yaml_path, encoding="utf-8") as f: data: dict[str, Any] = yaml.safe_load(f) or {} kis = data.get("kis", {}) if "server_mode" in kis: self.kis.server_mode = kis["server_mode"] rl = kis.get("rate_limit", {}) if "requests_per_second" in rl: self.rate_limit.requests_per_second = rl["requests_per_second"] if "retry_delay" in rl: self.rate_limit.retry_delay = rl["retry_delay"] collector = data.get("collector", {}) for key, val in collector.items(): if hasattr(self.collector, key): setattr(self.collector, key, val) strategy = data.get("strategies", {}) for key, val in strategy.items(): if hasattr(self.strategy, key): setattr(self.strategy, key, val) trading = data.get("trading", {}) for key, val in trading.items(): if hasattr(self.trading, key): setattr(self.trading, key, val) logging_cfg = data.get("logging", {}) if "level" in logging_cfg: self.app.log_level = logging_cfg["level"] settings = Settings() def get_base_url() -> str: if settings.kis.server_mode == "real": return "https://openapi.koreainvestment.com:9443" return "https://openapivts.koreainvestment.com:29443" def get_ws_url() -> str: if settings.kis.server_mode == "real": return "ws://ops.koreainvestment.com:21000" return "ws://ops.koreainvestment.com:31000"