""" 配置 Schema 定义 使用 Pydantic 验证配置文件的类型安全 """ from pydantic import BaseModel, Field, field_validator, model_validator from typing import Optional, Dict, List, Literal from enum import Enum from datetime import date # ============================================================ # 枚举类型 # ============================================================ # 注意:不再使用 MarketType 枚举,改用字符串类型的 group 字段 # group 字段用于策略分组(组内竞争,强制分散) class FactorType(str, Enum): """因子类型枚举""" MOMENTUM = "momentum" SLOPE_R2 = "slope_r2" WEIGHTED_MOMENTUM = "weighted_momentum" class PremiumMode(str, Enum): """溢价控制模式""" FILTER = "filter" # 完全排除 PENALIZE = "penalize" # 降权 class ThresholdMode(str, Enum): """阈值模式""" FIXED = "fixed" # 固定阈值 DYNAMIC = "dynamic" # 动态阈值 class DataSourceType(str, Enum): """数据源类型""" FLASK_API = "flask_api" TUSHARE = "tushare" YFINANCE = "yfinance" # ============================================================ # 资产池 Schema(扁平化设计) # ============================================================ class PremiumConfig(BaseModel): """溢价控制配置""" enabled: bool = Field(default=True, description="是否启用") threshold: float = Field(default=0.10, ge=0, le=1.0, description="溢价阈值") class AssetConfig(BaseModel): """ 标的配置(通用,支持所有场景) 场景 1:指数信号 → 指数收益 signal_source: "NDX" trade_source: "NDX" 场景 2:指数信号 → ETF收益 signal_source: "NDX" trade_source: "513100.SH" 场景 3:ETF信号 → ETF收益 signal_source: "518880.SH" trade_source: "518880.SH" 场景 4:个股信号 → 个股收益 signal_source: "AAPL" trade_source: "AAPL" """ name: str = Field(..., description="标的名称") group: str = Field(..., description="策略分组(组内竞争,强制分散)") # 核心字段:信号来源和交易来源 signal_source: str = Field(..., description="信号来源代码(计算因子用)") trade_source: str = Field(..., description="交易来源代码(计算收益用)") # 可选字段 description: Optional[str] = Field(None, description="标的描述") etf: Optional[str] = Field(None, description="ETF代码(兼容旧配置)") premium_control: Optional["PremiumConfig"] = Field(None, description="溢价控制配置") @property def is_cross_market(self) -> bool: """是否跨市场(信号和交易不同)""" return self.signal_source != self.trade_source class AssetPool(BaseModel): """ 资产池(扁平化设计) 优势: 1. 不预设分类,支持任意策略分组 2. 通过 group 字段自动分组 3. 配置简单直观 4. 易于扩展新分组 示例: asset_pool: assets: "NDX": name: "纳指100" group: "US_TECH" signal_source: "NDX" trade_source: "513100.SH" "BTC": name: "比特币" group: "CRYPTO" signal_source: "BTC" trade_source: "BTC" """ assets: Dict[str, AssetConfig] = Field( default_factory=dict, description="所有标的(flat结构)" ) @property def by_group(self) -> Dict[str, Dict[str, AssetConfig]]: """按策略分组""" groups = {} for code, asset in self.assets.items(): group = asset.group if group not in groups: groups[group] = {} groups[group][code] = asset return groups @property def groups(self) -> list: """获取所有策略分组""" return list(self.by_group.keys()) def get_signal_codes(self, group: str = None) -> list: """ 获取信号标的 Args: group: 可选,过滤特定分组(如 'US_TECH') """ if group: return [ asset.signal_source for asset in self.assets.values() if asset.group == group ] return [asset.signal_source for asset in self.assets.values()] def get_trade_codes(self, group: str = None) -> list: """获取交易标的""" if group: return [ asset.trade_source for asset in self.assets.values() if asset.group == group ] return [asset.trade_source for asset in self.assets.values()] def get_signal_to_trade_mapping(self, group: str = None) -> Dict[str, str]: """获取信号→交易映射""" mapping = {} for asset in self.assets.values(): if group and asset.group != group: continue mapping[asset.signal_source] = asset.trade_source return mapping def count(self, group: str = None) -> int: """获取标的数量""" if group: return len([a for a in self.assets.values() if a.group == group]) return len(self.assets) @field_validator('assets') @classmethod def check_non_empty(cls, v): """至少配置一个标的""" if not v: import warnings warnings.warn("资产池为空") return v # ============================================================ # 因子配置 Schema # ============================================================ class FactorConfig(BaseModel): """因子配置""" type: FactorType = Field(default=FactorType.WEIGHTED_MOMENTUM, description="因子类型") n_days: int = Field(default=25, ge=5, le=250, description="动量窗口期(天数)") # 动态周期参数 auto_day: bool = Field(default=False, description="是否启用动态周期") min_days: int = Field(default=20, ge=5, le=100, description="最小周期") max_days: int = Field(default=60, ge=20, le=250, description="最大周期") # ============================================================ # 阈值配置 Schema # ============================================================ class DynamicThresholdConfig(BaseModel): """动态阈值配置""" reference: str = Field(..., description="参考标的代码") ratio: float = Field(default=1.0, gt=0, description="倍数") fallback_enabled: bool = Field(default=True, description="参考不可用时是否回退") fallback_value: float = Field(default=0.0, description="回退值") class ThresholdConfig(BaseModel): """阈值配置(统一 V2/V3)""" mode: ThresholdMode = Field(default=ThresholdMode.DYNAMIC, description="阈值模式") fixed_value: float = Field(default=0.0, description="固定阈值(mode=fixed时使用)") dynamic: Optional[DynamicThresholdConfig] = Field(None, description="动态阈值(mode=dynamic时使用)") @model_validator(mode='after') def validate_dynamic_config(self): """验证动态阈值配置""" if self.mode == ThresholdMode.DYNAMIC and self.dynamic is None: raise ValueError("dynamic 模式必须配置 dynamic 字段") return self # ============================================================ # 轮动配置 Schema # ============================================================ class GroupConfig(BaseModel): """策略分组配置(用于分散化选股)""" group: str = Field(..., description="策略分组名称") select_num: int = Field(default=1, ge=1, le=10, description="该分组选股数量") class RotationConfig(BaseModel): """ 轮动配置 支持两种选股模式: 1. 全局选股:diversified=false,直接选 Top-N 2. 分散化选股:diversified=true,按策略分组选股 """ select_num: int = Field(default=3, ge=1, le=20, description="选股数量(全局模式)") diversified: bool = Field(default=False, description="是否分散化选股") # 分散化配置(可选) diversification_groups: Optional[List[GroupConfig]] = Field( None, description="分散化分组配置(diversified=true时使用)" ) threshold: ThresholdConfig = Field( default_factory=ThresholdConfig, description="动量阈值" ) # ============================================================ # 调仓配置 Schema # ============================================================ class RebalanceConfig(BaseModel): """调仓配置""" min_hold_days: int = Field(default=1, ge=1, le=30, description="最低持有天数") score_threshold: float = Field(default=0.0, ge=0, le=0.5, description="调仓得分阈值(%)") trade_cost: float = Field(default=0.001, ge=0, le=0.01, description="单次换仓成本") # ============================================================ # 溢价控制 Schema # ============================================================ class MarketPremiumOverride(BaseModel): """市场溢价覆盖配置""" enabled: bool = Field(default=True, description="是否启用") threshold: float = Field(default=0.10, ge=0, le=1.0, description="溢价阈值") class PremiumControlConfig(BaseModel): """溢价控制配置""" enabled: bool = Field(default=True, description="是否启用溢价控制") default_threshold: float = Field(default=0.10, ge=0, le=1.0, description="默认溢价阈值") mode: PremiumMode = Field(default=PremiumMode.FILTER, description="控制模式") penalty_factor: float = Field(default=0.5, ge=0, le=1.0, description="降权惩罚系数") # 按市场覆盖 market_overrides: Dict[str, MarketPremiumOverride] = Field( default_factory=dict, description="按市场覆盖配置" ) # ============================================================ # 数据源配置 Schema # ============================================================ class DataSourceConfig(BaseModel): """单个数据源配置""" type: DataSourceType = Field(..., description="数据源类型") enabled: bool = Field(default=True, description="是否启用") timeout: int = Field(default=120, ge=10, le=600, description="超时时间(秒)") # Flask API 特定配置 url: Optional[str] = Field(None, description="API地址(flask_api使用)") # Tushare 特定配置 token: Optional[str] = Field(None, description="Tushare Token(tushare使用)") class DataConfig(BaseModel): """数据配置""" sources: List[DataSourceConfig] = Field(..., description="数据源列表(按优先级排序)") use_cache: bool = Field(default=True, description="是否使用本地缓存") cache_dir: str = Field(default="data_cache", description="缓存目录") @field_validator('sources') @classmethod def check_at_least_one(cls, v): """至少配置一个数据源""" if not v: raise ValueError("必须配置至少一个数据源") return v # ============================================================ # 基准配置 Schema # ============================================================ class BenchmarkConfig(BaseModel): """基准配置""" code: str = Field(..., description="基准代码") name: str = Field(..., description="基准名称") # ============================================================ # 回测配置 Schema # ============================================================ class BacktestConfig(BaseModel): """回测配置""" start_date: str = Field(..., description="回测起始日期(YYYY-MM-DD)") end_date: Optional[str] = Field(None, description="回测结束日期(None表示至今)") # ============================================================ # 元数据 Schema # ============================================================ class MetadataConfig(BaseModel): """配置元数据""" version: str = Field(default="1.0.0", description="配置版本") strategy: str = Field(default="rotation", description="策略名称") description: str = Field(default="", description="配置描述") last_updated: Optional[str] = Field(None, description="最后更新日期") # ============================================================ # 完整策略配置 Schema # ============================================================ class RotationStrategyConfig(BaseModel): """ ETF轮动策略完整配置 使用示例: from framework_v2.config.schemas import RotationStrategyConfig import yaml with open('config/rotation.yaml') as f: config_dict = yaml.safe_load(f) config = RotationStrategyConfig(**config_dict) """ metadata: MetadataConfig = Field(default_factory=MetadataConfig, description="元数据") # 资产池 asset_pools: AssetPool = Field(..., description="资产池配置") # 基准 benchmark: BenchmarkConfig = Field(..., description="基准配置") # 回测 backtest: BacktestConfig = Field(..., description="回测配置") # 因子 factor: FactorConfig = Field(default_factory=FactorConfig, description="因子配置") # 轮动 rotation: RotationConfig = Field(default_factory=RotationConfig, description="轮动配置") # 调仓 rebalance: RebalanceConfig = Field(default_factory=RebalanceConfig, description="调仓配置") # 溢价控制 premium_control: PremiumControlConfig = Field(default_factory=PremiumControlConfig, description="溢价控制") # 数据 data: DataConfig = Field(..., description="数据配置") # ============================================================ # 通用策略配置 Schema(V2 架构) # ============================================================ class StrategyConfig(BaseModel): """ 通用策略配置(支持所有策略类型) 使用示例: from framework_v2.config import load_config config = load_config('rotation.yaml') """ metadata: MetadataConfig = Field(default_factory=MetadataConfig, description="元数据") # 资产池 asset_pools: AssetPool = Field(..., description="资产池配置") # 基准 benchmark: BenchmarkConfig = Field(..., description="基准配置") # 回测 backtest: BacktestConfig = Field(..., description="回测配置") # 因子 factor: FactorConfig = Field(default_factory=FactorConfig, description="因子配置") # 轮动(可选) rotation: Optional[RotationConfig] = Field(None, description="轮动配置") # 调仓 rebalance: RebalanceConfig = Field(default_factory=RebalanceConfig, description="调仓配置") # 溢价控制 premium_control: PremiumControlConfig = Field(default_factory=PremiumControlConfig, description="溢价控制") # 数据 data: DataConfig = Field(..., description="数据配置")