chore: 重构前基线 — 9 个 sync task 全部 ok + akshare 移除 + mairui 资金流接入
状态: - 9 个 sync task(stock_basic / kline_daily / kline_index / kline_5min / moneyflow / industry_sector / sector_features / share_snapshot / market_regime) - 数据源:baostock + mairui + 雪球(pysnowball) + 新浪(4 个) - 项目级约束:永远不用 akshare(已落实) - kline_5min 改用 DB 快照统一全量/增量逻辑 - 零后端 Chrome 扩展 xueqiu_sync(独立项目)
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
"""统一配置:从 .env / 环境变量读 MySQL、调度器、数据源开关等参数。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
try:
|
||||
from dotenv import load_dotenv
|
||||
_HAS_DOTENV = True
|
||||
except ImportError:
|
||||
_HAS_DOTENV = False
|
||||
|
||||
|
||||
_PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
# 启动时自动把 .env 加载到 os.environ,使下游 `os.environ.get(...)` 代码
|
||||
# (如 app/sources/mairui.py / xueqiu.py)能拿到 credentials。
|
||||
if _HAS_DOTENV:
|
||||
_env_file = _PROJECT_ROOT / ".env"
|
||||
if _env_file.exists():
|
||||
load_dotenv(_env_file, override=False)
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""应用配置。所有字段都可以通过环境变量或 .env 文件覆盖。"""
|
||||
|
||||
# MySQL
|
||||
mysql_host: str = "127.0.0.1"
|
||||
mysql_port: int = 3306
|
||||
mysql_user: str = "root"
|
||||
mysql_password: str = ""
|
||||
# 同步任务写入的目标库(原始市场数据)
|
||||
mysql_database: str = "market_data_sync_db"
|
||||
# 简版库(对照测试,可选)
|
||||
mysql_database_lite: str = "grid_seeker"
|
||||
# 业务主库(保留— 同步任务不再写入)
|
||||
mysql_database_business: str = "grid_seeker_model_base"
|
||||
|
||||
# Scheduler
|
||||
scheduler_tick_seconds: int = 5
|
||||
scheduler_timezone: str = "Asia/Shanghai"
|
||||
scheduler_auto_seed: bool = True
|
||||
|
||||
# API
|
||||
api_host: str = "0.0.0.0"
|
||||
api_port: int = 8100
|
||||
|
||||
# Logging
|
||||
log_level: str = "INFO"
|
||||
log_dir: str = "./logs"
|
||||
|
||||
# Datasources
|
||||
ds_baostock_enabled: bool = True
|
||||
ds_sina_enabled: bool = True
|
||||
xueqiu_token: str = ""
|
||||
|
||||
# Trading calendar
|
||||
trading_holidays: str = ""
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=str(_PROJECT_ROOT / ".env"),
|
||||
env_file_encoding="utf-8",
|
||||
case_sensitive=False,
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
@property
|
||||
def project_root(self) -> Path:
|
||||
return _PROJECT_ROOT
|
||||
|
||||
@property
|
||||
def log_path(self) -> Path:
|
||||
p = Path(self.log_dir)
|
||||
if not p.is_absolute():
|
||||
p = _PROJECT_ROOT / p
|
||||
p.mkdir(parents=True, exist_ok=True)
|
||||
return p
|
||||
|
||||
@property
|
||||
def holidays_list(self) -> list[str]:
|
||||
if not self.trading_holidays:
|
||||
return []
|
||||
return [d.strip() for d in self.trading_holidays.split(",") if d.strip()]
|
||||
|
||||
def mysql_url(self, database: Optional[str] = None) -> str:
|
||||
"""构造 SQLAlchemy URL。"""
|
||||
db = database or self.mysql_database
|
||||
return (
|
||||
f"mysql+pymysql://{self.mysql_user}:{self.mysql_password}"
|
||||
f"@{self.mysql_host}:{self.mysql_port}/{db}?charset=utf8mb4"
|
||||
)
|
||||
|
||||
def pymysql_connect_kwargs(self, database: Optional[str] = None) -> dict:
|
||||
"""构造 pymysql.connect 的关键字参数。
|
||||
|
||||
内网自签名证书场景自动禁用 SSL。
|
||||
"""
|
||||
db = database or self.mysql_database
|
||||
return {
|
||||
"host": self.mysql_host,
|
||||
"port": self.mysql_port,
|
||||
"user": self.mysql_user,
|
||||
"password": self.mysql_password,
|
||||
"database": db,
|
||||
"charset": "utf8mb4",
|
||||
"autocommit": False,
|
||||
# 兼容自签名证书的内网 MySQL — 禁用 SSL
|
||||
"ssl": None,
|
||||
}
|
||||
|
||||
|
||||
_settings: Optional[Settings] = None
|
||||
|
||||
|
||||
def get_settings() -> Settings:
|
||||
"""全局单例。"""
|
||||
global _settings
|
||||
if _settings is None:
|
||||
_settings = Settings()
|
||||
return _settings
|
||||
|
||||
|
||||
settings = get_settings()
|
||||
Reference in New Issue
Block a user