feat(portfolio): M8.2 Portfolio Engine 模块化(等权收敛 + 约束显式标注)
- research.PortfolioSpec(weighting=equal;max_position_pct/max_industry_weight_pct 预留) + ResearchSpec.portfolio;config_snapshot 自动记录组合配置 - quant/portfolio.py:equal_weight_budget(与既有等权回测语义一致,行为收敛到本模块)+ unimplemented_notes(设置约束即在结果中显式标注未建模,禁止假装支持) - TopKBacktestRunner 预算与 unimplemented 改用 portfolio 模块;默认配置数值不变 (一致性/quant 引擎回归通过);tests 补约束标注与 config_snapshot;全量 pytest 通过
This commit is contained in:
@@ -123,3 +123,26 @@ class TestSelectionBacktestConsistency:
|
||||
held, key=lambda s: res.candidates[[x.symbol for x in res.candidates].index(s)].score,
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
|
||||
class TestPortfolioEngine:
|
||||
def test_equal_weight_default_unchanged(self, daily_df) -> None:
|
||||
"""新增 PortfolioSpec 后默认配置回测结果与未设置前一致(回归由本文件首测已锁数值)。"""
|
||||
from app.domain.entities.research import PortfolioSpec
|
||||
from app.quant.engine import LocalEngine
|
||||
|
||||
spec = _spec(portfolio=PortfolioSpec())
|
||||
result = LocalEngine().run_backtest(daily_df, spec)
|
||||
assert result.summary.total_trades >= 0
|
||||
# 未设约束 → 无组合约束说明
|
||||
assert not any("约束未建模" in u for u in result.unimplemented)
|
||||
|
||||
def test_constraint_declared_in_unimplemented(self, daily_df) -> None:
|
||||
from app.domain.entities.research import PortfolioSpec
|
||||
from app.quant.engine import LocalEngine
|
||||
|
||||
spec = _spec(portfolio=PortfolioSpec(max_position_pct=0.1))
|
||||
result = LocalEngine().run_backtest(daily_df, spec)
|
||||
assert any("最大单股权重" in u for u in result.unimplemented)
|
||||
# config_snapshot 记录组合配置
|
||||
assert result.config_snapshot["portfolio"]["max_position_pct"] == 0.1
|
||||
|
||||
Reference in New Issue
Block a user