SPB Git forge
1commits 1branches 0releases
189.0 KBsize
maindefault branch
4 h agolast push
Python 54.7% TypeScript 42.5% SQL 1.9%
2.9 KB · 80 lines python
Raw Blame History
1"""Runtime configuration (environment-injected; never log secrets)."""23from __future__ import annotations45from functools import lru_cache6from typing import Annotated, Literal78from pydantic import Field, field_validator9from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict1011TradingMode = Literal["offline", "paper", "live"]121314class Settings(BaseSettings):15    model_config = SettingsConfigDict(env_prefix="KTRADE_", env_file=".env", extra="ignore")1617    env: str = "dev"18    app_base_url: str = "http://127.0.0.1:8471"19    api_host: str = "127.0.0.1"20    api_port: int = 847021    log_level: str = "INFO"22    log_json: bool = True2324    database_url: str = "postgresql+asyncpg://ktrade:ktrade@127.0.0.1:5432/ktrade"25    redis_url: str = "redis://127.0.0.1:6379/3"2627    # Session / auth28    secret_key: str = Field(default="dev-insecure-change-me", description="Signs session cookies")29    owner_password_hash: str = Field(default="", description="bcrypt hash of the owner password")30    session_ttl_seconds: int = 12 * 360031    cookie_secure: bool = False3233    # Kraken — three least-privilege key pairs. Data/account keys must NOT have trade permission;34    # the trade key is only read when live mode is armed. Withdrawal permission is never used.35    kraken_rest_url: str = "https://api.kraken.com"36    kraken_ws_url: str = "wss://ws.kraken.com/v2"37    kraken_ws_auth_url: str = "wss://ws-auth.kraken.com/v2"38    kraken_api_key: str = ""39    kraken_api_secret: str = ""40    kraken_trade_api_key: str = ""41    kraken_trade_api_secret: str = ""42    kraken_tier: Literal["starter", "intermediate", "pro"] = "starter"4344    # Universe & ingestion45    universe: Annotated[list[str], NoDecode] = Field(default_factory=lambda: ["BTC/USD", "ETH/USD", "SOL/USD", "XRP/USD", "ADA/USD", "LINK/USD", "DOT/USD", "LTC/USD"])46    candle_intervals: Annotated[list[int], NoDecode] = Field(default_factory=lambda: [1, 5, 60, 1440])47    backfill_poll_seconds: int = 6048    book_depth: int = 2549    stale_data_seconds: int = 1205051    # Trading mode gates52    trading_mode: TradingMode = "offline"53    live_trading_enabled: bool = False  # feature flag; UI activation also required (Phase 5)5455    # Observability56    metrics_enabled: bool = True5758    @field_validator("universe", "candle_intervals", mode="before")59    @classmethod60    def _split_csv(cls, v):  # noqa: ANN00161        if isinstance(v, str):62            parts = [p.strip() for p in v.split(",") if p.strip()]63            return [int(p) if p.isdigit() else p for p in parts]64        return v6566    @property67    def sync_database_url(self) -> str:68        return self.database_url.replace("+asyncpg", "")6970    def has_data_key(self) -> bool:71        return bool(self.kraken_api_key and self.kraken_api_secret)7273    def has_trade_key(self) -> bool:74        return bool(self.kraken_trade_api_key and self.kraken_trade_api_secret)757677@lru_cache78def get_settings() -> Settings:79    return Settings()80