Files
stock/backend/app/config.py

61 lines
2.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""应用配置pydantic-settings。可由 .env / 环境变量覆盖。"""
from pydantic import model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
app_name: str = "Stock Backtest"
# 开发、测试、生产统一使用 PostgreSQL避免不同数据库行为产生偏差。
database_url: str = "postgresql+asyncpg://postgres:postgres@localhost:5432/stock"
# ---- 鉴权 ----
auth_cookie_name: str = "stock_session"
auth_cookie_secure: bool = False # 生产 HTTPS 必须覆盖为 true
auth_session_hours: int = 12
auth_max_failed_logins: int = 5
auth_lock_minutes: int = 15
auth_min_password_length: int = 12
cors_origins: str = "http://localhost:5173"
expose_api_docs: bool = True
# 真实数据源
tushare_token: str = "" # Tushare Pro token主数据源
data_adjust: str = "qfq" # 复权qfq 前复权 / hfq 后复权 / "" 不复权
data_default_start: str = "20200101" # 默认拉取起点(约近 5 年)
# ---- LLM智能选股的自然语言解析DeepSeekOpenAI 兼容协议,可换任意兼容网关)----
llm_base_url: str = "https://api.deepseek.com"
llm_api_key: str = "" # 留空则智能选股不可用(其余功能不受影响)
llm_model: str = "deepseek-chat"
llm_timeout: float = 60.0
# ---- 智能选股 ----
screener_market_days: int = 90 # 全市场同步窗口(交易日数)
screener_default_limit: int = 200 # 选股结果条数上限
screener_sync_interval: float = 0.35 # 全市场批量调用间隔Tushare 控频
# A股交易成本基准日 2026-08——做成可配置参数便于将来按生效日期版本化
stamp_duty_rate: float = 0.0005 # 印花税 0.05%单边卖出2023-08-28 减半)
transfer_fee_rate: float = 0.00001 # 过户费 0.001%沪深双边2022 调整)
commission_rate: float = 0.0001 # 佣金 万1含规费
commission_min: float = 5.0 # 最低 5 元
slippage_rate: float = 0.0005 # 滑点近似(按价格比例)
@model_validator(mode="after")
def require_postgresql(self) -> "Settings":
if not self.database_url.startswith("postgresql+asyncpg://"):
raise ValueError("DATABASE_URL 必须使用 postgresql+asyncpg://,本项目不再支持 SQLite")
if self.auth_session_hours <= 0:
raise ValueError("AUTH_SESSION_HOURS 必须大于 0")
return self
@property
def allowed_origins(self) -> list[str]:
return [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()]
settings = Settings()