Files
qlib/backend/app/quant/engine.py
T
Simon 0d3e123de3 feat(selection): M6.4 回测与选股共用评分引擎(v2 §25 一致性锁定)
- quant/selection.score_panel_for_factors:复合分面板构建收敛为共享函数;
  LocalEngine.run_backtest 与 SelectionEngine.run_score_selection 均调它 ——
  消除「回测一套评分、选股另一套」的隐患
- tests/test_selection_backtest_consistency.py:对回测每个调仓日验证
  SelectionService.select(as_of=d, top_n) 候选 == 该日回测实际持仓(月调仓多时点),
  排序方向一致性亦验证;全量 pytest 通过
2026-09-09 00:22:08 +08:00

67 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""研究引擎抽象与默认实现(业务层依赖本接口,可替换引擎)。
切换引擎(如未来在支持平台启用 Qlib)只需注入不同实现 —— 业务代码不变。
"""
from __future__ import annotations
from typing import Protocol
import pandas as pd
from app.domain.entities.research import BacktestResult, FactorTestReport, ResearchSpec
from app.quant.factors import FactorError, get_factor
from app.quant.local_engine import TopKBacktestRunner, run_spec_factor_test
from app.quant.selection import score_panel_for_factors
# LocalEngine 路径恒需 close(TopK 收盘撮合 / 前瞻收益)
_CLOSE = {"close"}
def factor_required_columns(spec: ResearchSpec) -> set[str]:
"""spec 因子计算 + 回测撮合所需的行情数值列(含 close)。"""
needed = set(_CLOSE)
for fs in spec.factors:
try:
defn, _fn = get_factor(fs.name)
except FactorError:
continue # 未知因子由执行期统一报错
needed.update(defn.requires)
return needed
class QuantEngine(Protocol):
"""研究引擎端口:因子面板构建 / 因子测试 / 回测。"""
name: str
def required_columns(self, spec: ResearchSpec) -> set[str]:
"""执行该 spec 所需的行情数值列(数据装配按此裁剪,控制内存)。"""
def run_factor_test(
self, daily: pd.DataFrame, spec: ResearchSpec, horizon_days: int = 21
) -> FactorTestReport: ...
def run_backtest(self, daily: pd.DataFrame, spec: ResearchSpec) -> BacktestResult: ...
class LocalEngine:
"""默认引擎:纯 pandas 实现(无 Qlib 依赖),见 local_engine.py 的纪律说明。"""
name = "local"
def required_columns(self, spec: ResearchSpec) -> set[str]:
return factor_required_columns(spec)
def run_factor_test(
self, daily: pd.DataFrame, spec: ResearchSpec, horizon_days: int = 21
) -> FactorTestReport:
report, _panels = run_spec_factor_test(daily, spec, horizon_days)
return report
def run_backtest(self, daily: pd.DataFrame, spec: ResearchSpec) -> BacktestResult:
# 评分面板与选股共用同一构建(v2 §25:回测与当前选股同引擎)
score = score_panel_for_factors(daily, spec.factors)
close = daily.pivot(index="trade_date", columns="symbol", values="close").sort_index()
return TopKBacktestRunner(spec, score, close).run()