Some checks failed
CI / test (pull_request) Has been cancelled
Implement comprehensive multi-market trading system with automatic market selection based on timezone and trading hours. ## New Features - Market schedule module with 10 global markets (KR, US, JP, HK, CN, VN) - Overseas broker for KIS API international stock trading - Automatic market detection based on current time and timezone - Next market open waiting logic when all markets closed - ConnectionError retry with exponential backoff (max 3 attempts) ## Architecture Changes - Market-aware trading cycle with domestic/overseas broker routing - Market context in AI prompts for better decision making - Database schema extended with market and exchange_code columns - Config setting ENABLED_MARKETS for market selection ## Testing - 19 new tests for market schedule (timezone, DST, lunch breaks) - All 54 tests passing - Lint fixes with ruff ## Files Added - src/markets/schedule.py - Market schedule and timezone logic - src/broker/overseas.py - KIS overseas stock API client - tests/test_market_schedule.py - Market schedule test suite ## Files Modified - src/main.py - Multi-market main loop with retry logic - src/config.py - ENABLED_MARKETS setting - src/db.py - market/exchange_code columns with migration - src/brain/gemini_client.py - Dynamic market context in prompts Resolves #5 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
53 lines
1.5 KiB
Python
53 lines
1.5 KiB
Python
"""Strictly typed configuration loaded from environment variables."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pydantic import Field
|
|
from pydantic_settings import BaseSettings
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""Application settings — loaded from .env or environment variables."""
|
|
|
|
# KIS Open API
|
|
KIS_APP_KEY: str
|
|
KIS_APP_SECRET: str
|
|
KIS_ACCOUNT_NO: str # format: "XXXXXXXX-XX"
|
|
KIS_BASE_URL: str = "https://openapivts.koreainvestment.com:9443"
|
|
|
|
# Google Gemini
|
|
GEMINI_API_KEY: str
|
|
GEMINI_MODEL: str = "gemini-pro"
|
|
|
|
# Risk Management
|
|
CIRCUIT_BREAKER_PCT: float = Field(default=-3.0, le=0.0)
|
|
FAT_FINGER_PCT: float = Field(default=30.0, gt=0.0, le=100.0)
|
|
CONFIDENCE_THRESHOLD: int = Field(default=80, ge=0, le=100)
|
|
|
|
# Database
|
|
DB_PATH: str = "data/trade_logs.db"
|
|
|
|
# Rate Limiting (requests per second for KIS API)
|
|
RATE_LIMIT_RPS: float = 10.0
|
|
|
|
# Trading mode
|
|
MODE: str = Field(default="paper", pattern="^(paper|live)$")
|
|
|
|
# Market selection (comma-separated market codes)
|
|
ENABLED_MARKETS: str = "KR"
|
|
|
|
model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}
|
|
|
|
@property
|
|
def account_number(self) -> str:
|
|
return self.KIS_ACCOUNT_NO.split("-")[0]
|
|
|
|
@property
|
|
def account_product_code(self) -> str:
|
|
return self.KIS_ACCOUNT_NO.split("-")[1]
|
|
|
|
@property
|
|
def enabled_market_list(self) -> list[str]:
|
|
"""Parse ENABLED_MARKETS into list of market codes."""
|
|
return [m.strip() for m in self.ENABLED_MARKETS.split(",") if m.strip()]
|