"""选股策略领域实体(2026-09 重构:原 StrategyDefinition → SelectionStrategy)。 策略库现在**只存选股条件组合**:股票池 + 因子 + 过滤条件。 资金 / 持仓数 / 持仓时间 / 调仓时机 / 费率 / 回测区间一律移到「回测组合」 (BacktestCombo)与「公共配置」(GlobalConfig),在回测时才确定。 仍映射到 `strategy` 表(id/name/description/spec_type/config_json/version/created_at), config_json 只存 universe/factors/conditions —— 迁移会把旧行的多余键剥掉。 """ from __future__ import annotations from datetime import datetime from pydantic import BaseModel, Field, model_validator from app.domain.entities.research import ConditionSpec, FactorSpec, UniverseSpec class SelectionStrategy(BaseModel): """一个选股策略 = 选股条件组合(不含任何回测执行参数)。""" id: str = "" name: str = Field(min_length=1, max_length=64) description: str = "" spec_type: str = Field(default="selection", pattern="^(selection|backtest)$") universe: UniverseSpec = UniverseSpec() factors: list[FactorSpec] = Field(min_length=1, description="打分因子(至少 1 个)") conditions: list[ConditionSpec] = Field( default_factory=list, description="过滤条件(AND,universe 之后、因子排序之前)" ) version: str = "1" created_at: datetime | None = None @model_validator(mode="after") def _no_duplicate_factors(self) -> "SelectionStrategy": names = [f.name for f in self.factors] if len(set(names)) != len(names): raise ValueError("factors 存在重复因子名") return self # 兼容别名:重构前到处引用的旧名字。新代码请用 SelectionStrategy; # 保留别名是为了让尚未迁移的导入点(agent 工具等)在过渡期不炸, # 最终会全部替换掉(见各调用点的 TODO)。 StrategyDefinition = SelectionStrategy