- StrategyDefinition:universe/factors/selection/rebalance/costs/portfolio +
price_adjustment(除 period 外完整策略定义);to_research_spec(period) 展开为标准 Spec
- strategy 表(migration e1f2a3b4c5d6,MySQL 已应用;name 唯一)+ StrategyRepository
- /api/strategies:POST/GET/DELETE + POST /{id}/expand(period+initial_capital → ResearchSpec)
- tests/test_strategies.py(repo CRUD/同名/expand、API CRUD/400/404);全量 pytest 通过
54 lines
1.9 KiB
Python
54 lines
1.9 KiB
Python
"""策略领域实体(M8.3,v2 §17/§5.3)。
|
||
|
||
Strategy = 完整策略定义(universe + factors + selection + rebalance + costs +
|
||
portfolio,除回测区间 period 外),保存为命名资产;回测时补 period 展开为
|
||
ResearchSpec(v2 §18 Research Specification 为统一契约,策略是其持久化形态)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import date, datetime
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
from app.domain.entities.research import (
|
||
CostSpec,
|
||
FactorSpec,
|
||
PortfolioSpec,
|
||
SelectionSpec,
|
||
UniverseSpec,
|
||
)
|
||
|
||
|
||
class StrategyDefinition(BaseModel):
|
||
id: str = ""
|
||
name: str = Field(min_length=1, max_length=64)
|
||
description: str = ""
|
||
spec_type: str = Field(default="backtest", pattern="^(backtest|factor_test)$")
|
||
universe: UniverseSpec = UniverseSpec()
|
||
price_adjustment: str = Field(default="none", pattern="^(none|qfq)$")
|
||
factors: list[FactorSpec] = Field(min_length=1)
|
||
selection: SelectionSpec = SelectionSpec()
|
||
rebalance: str = Field(default="monthly", pattern="^(weekly|monthly)$")
|
||
costs: CostSpec = CostSpec()
|
||
portfolio: PortfolioSpec = PortfolioSpec()
|
||
version: str = "1"
|
||
created_at: datetime | None = None
|
||
|
||
def to_research_spec(self, period: tuple[date, date], initial_capital: float | None = None):
|
||
"""补全回测区间/资金后展开为标准 ResearchSpec(可在 Job/回测执行)。"""
|
||
from app.domain.entities.research import ResearchSpec
|
||
|
||
return ResearchSpec(
|
||
type=self.spec_type,
|
||
universe=self.universe,
|
||
price_adjustment=self.price_adjustment,
|
||
factors=self.factors,
|
||
selection=self.selection,
|
||
rebalance=self.rebalance,
|
||
period=period,
|
||
costs=self.costs,
|
||
portfolio=self.portfolio,
|
||
initial_capital=initial_capital if initial_capital else 1_000_000.0,
|
||
)
|