17 KiB
quant_engine
量化研究引擎 —— alpha 因子库 + 执行仿真 + 技术指标 + 数据适配 + 回测工具
从 research_results 抽出的纯回测能力库(v1.2.0 重构)。
角色
quant_engine 是 researchhub_workspace 的引擎层:
| 仓库 | 角色 |
|---|---|
quant_engine |
纯研究核心(alpha + execution + ledger + attribution + risk + metrics) |
research_results |
业务集成(47 个 proj 调度 + 注册 + 平台对接) |
tushare2db_pro_aoge |
数据层(行情 ELT) |
research_platform |
展示层(FastAPI + Next.js) |
edb_data_core |
数据层(经济数据) |
模块
alpha_factors— 158 alpha 公式 + 24 基础算子(移植自 qlib alpha158)factor_contracts—FactorDefinition/FactorSetRefv1 纯计算合同、严格 PIT/availability 输入准入与显式 legacy 投影execution— A 股长仓执行仿真(成本/滑点/现金约束)+ 稀疏调仓/完整交易日 Ledger + 可投影成交与 NAV 审计;T+1、涨跌停、成交量与价差提供独立约束函数indicators— 50+ 技术指标(MACD / KDJ / 布林 / ATR / ADX / 等)data_adapter— 桥接 qtdb_pro 长表与新模块(rename / long-wide / 复权 / vwap 代理)backtest— weight-based 多日仿真(rebalance_table / compute_nav / compare_to_benchmark)portfolio_construction— 多期因子分数 → Top-K → 等权目标权重表research_pipeline— 因子日 → 下一真实交易日 → 显式执行价 → 日末估值 → 成本后绩效(防前视编排)governed_pipeline— 数据快照 → 因子版本 → 策略版本 → 回测运行 → 目标组合 → 风险决策 → Paper 订单意图;同时拥有输入/配置/重放血缘决定的BacktestRunRefartifact— 版本化、确定性、存储中立的完整 research run 事实表,以及只映射现有表的BacktestEvidenceManifestportfolio_risk_contracts— S3 证据闭合的PortfolioDecision/RiskAssessmentv1;独立复核 freshness、约束与 computation receipt,并复用既有标签安全风险分解attribution— 基于实际成交后持仓的隔夜 / 日内 / 交易成本逐日收益归因与闭合审计metrics— 绝对绩效 + 严格日期对齐的 TE / IR / alpha / beta 基准相对绩效factor_library— 通用方法(turnover / winsorize / IC / OLS / jb_test)portfolio_decomp— 组合分解(risk_parity / mean_variance / 因子归因)risk— ndarray 低层风险公式 + 标签安全、可分组的 Euler 成分风险分解perf_stats— 详细绩效(与 metrics 并存)logging— 统一 logger(标准库 + 可选 loguru)
依赖
- 必需:numpy / pandas / scipy(标准量化栈)
- 可选:loguru(logback,标准库 logging 兜底)
零重型依赖 —— 不引入 torch / lightgbm / hikyuu 等。
安装
cd quant_engine
pip install -e ".[dev]"
测试
pytest # 单元测试
pytest --cov=src # 覆盖率
mypy --strict src/ # 类型检查
ruff check src/ tests/ # lint
# 无网络、无数据库、无券商的架构烟测
uv run python -m quant_engine.governed_pipeline
使用
from quant_engine.alpha_factors import alpha_001, alpha_005, ALPHA158_REGISTRY
from quant_engine.execution import (
ExecutionConfig, simulate_daily_ledger_with_audit,
simulate_multi_day_with_audit, simulate_with_daily_data,
)
from quant_engine.research_pipeline import (
run_factor_backtest_research, run_factor_execution_research,
)
from quant_engine.backtest import run_weight_backtest
from quant_engine.indicators import macd, bollinger, kdj
from quant_engine.data_adapter import (
long_to_wide, wide_to_long, rename_tushare_columns,
add_vwap_proxy, apply_adj_factor,
prepare_stock_series, prepare_execution_inputs,
load_qtdb_daily,
)
# 端到端:qtdb_pro 长表 → 适配 → alpha158 → execution
df = load_qtdb_daily(["000001.SZ"], "2024-01-01", with_adj=True)
close_prices, volumes = prepare_execution_inputs(df)
open_prices, _ = prepare_execution_inputs(df, price_col="open")
result = simulate_with_daily_data(close_prices, initial_cash=1_000_000.0)
# 已正确滞后的目标权重 → 现金约束执行 → 唯一来源的成交/拒绝/日末持仓/NAV
execution = simulate_multi_day_with_audit(
target_weights_history=[
("2024-01-02", {"000001.SZ": 1.0}),
("2024-01-03", {"000001.SZ": 1.0}),
],
price_history=[
("2024-01-02", {"000001.SZ": 10.0}),
("2024-01-03", {"000001.SZ": 10.5}),
],
initial_cash=1_000_000.0,
config=ExecutionConfig(),
)
print(execution.nav_series)
print(execution.daily_executions)
# 多期因子分数(必须是 point-in-time 数据)→ Top-K → 下一交易日 open 执行
factor_execution = run_factor_execution_research(
factor_scores,
top_k=20,
execution_prices=open_prices,
execution_price_field="open",
initial_cash=1_000_000.0,
)
# 推荐研究入口:同一交易日历上显式区分 open 成交和 close 估值。
# 因子日保持现金,下一交易日成交后的真实持仓才参与当日收盘收益。
factor_backtest = run_factor_backtest_research(
factor_scores,
top_k=20,
execution_prices=open_prices,
valuation_prices=close_prices,
execution_price_field="open",
valuation_price_field="close",
initial_cash=1_000_000.0,
config=ExecutionConfig(),
)
print(factor_backtest.nav)
print(factor_backtest.returns)
print(factor_backtest.stats())
print(factor_backtest.execution.ledger_frame)
print(factor_backtest.execution.trades_frame)
print(factor_backtest.position_weights) # 实际日末资产权重
print(factor_backtest.cash_weights)
# 所有分析都以实际成交后的 Ledger 为事实源,不直接使用目标权重伪造结果。
attribution = factor_backtest.return_attribution()
print(attribution.asset_contributions)
print(attribution.transaction_cost)
print(attribution.residual) # 应接近 0;否则说明贡献未闭合到账本收益
# benchmark_returns 必须与成本后 factor_backtest.returns 使用完全相同的日期索引。
print(factor_backtest.benchmark_stats(benchmark_returns))
# 下游稳定交付:显式提供代码版本、数据快照和时区,不在核心层写数据库。
from quant_engine.artifact import build_research_run_artifact
from quant_engine.data_adapter import prepare_asset_return_snapshot
from quant_engine.risk import estimate_covariance_snapshot
risk_date = factor_backtest.position_weights.index[-1].date()
market_snapshot = prepare_asset_return_snapshot(
qtdb_daily_long,
source="qtdb_pro.hq_daily",
source_snapshot_id="<upstream-ingestion-snapshot-id>",
adjustment="qfq",
)
risk_snapshot = estimate_covariance_snapshot(
market_snapshot.returns,
as_of_date=risk_date,
lookback_sessions=252,
min_observations=120,
data_snapshot_id=market_snapshot.data_snapshot_id,
)
artifact = build_research_run_artifact(
factor_backtest,
run_id="research-run-001",
strategy_id="alpha-top20",
strategy_name="Alpha Top 20",
strategy_version="1.0.0",
engine_version="1.2.0",
code_revision="<git-sha>",
data_snapshot_id=market_snapshot.data_snapshot_id,
calendar="CN-A",
timezone="Asia/Shanghai",
started_at="2026-08-21T10:00:00+08:00",
finished_at="2026-08-21T10:01:00+08:00",
parameters={"top_k": 20, "lag_sessions": 1},
benchmark_id="000300.SH",
benchmark_returns=benchmark_returns,
risk_snapshots={risk_date: risk_snapshot},
)
print(artifact.manifest())
# run_weight_backtest 是低层算子:只接受收益区间开始前已经生效的持仓权重。
# 不要把 signal-date 的 factor_scores/decision_weights 直接传给它。
backtest = run_weight_backtest(
weights=effective_holding_weights,
stock_returns=daily_returns,
initial_capital=1_000_000.0,
benchmark_nav=benchmark_nav,
)
print(factor_execution.schedule.signal_to_execution)
print(factor_execution.execution.daily_executions)
print(backtest.stats())
print(backtest.benchmark_report())
因子/特征合同 v1
quant_engine.factor_contracts 提供 researchhub.factor-definition 与
researchhub.factor-set-ref 1.0.0。合同使用受限 canonical JSON:只接受 ASCII
lower-snake-case object key、UTF-8 string、bool/null 和 safe integer;小数参数必须用显式
canonical decimal string。定义、输入映射、上游证据、输出 schema/content 和 lineage 的任一
语义变化都会产生新 identity。
创建 FactorSetRef 必须提供完整且可重算 identity 的 DatasetSnapshotEnvelope 与
DataFoundationEnvelope,不能用 ID 字符串或布尔值代替资格证明。每个因子输入都要映射到一个
实际选中的 StandardizedViewRef,schema 必须同时匹配定义和 view;未消费、缺失、重复或跨
snapshot/Foundation/PIT 的 view 都会失败关闭。snapshot PIT 可以早于 Foundation/view PIT,
但始终满足 knowledge ≤ snapshot PIT ≤ Foundation/view/FactorSet PIT ≤ evaluation。
from quant_engine.factor_contracts import (
DataFoundationEnvelope,
DatasetSnapshotEnvelope,
FactorDefinition,
FactorSetRef,
)
snapshot = DatasetSnapshotEnvelope.from_dict(dataset_snapshot_v1)
foundation = DataFoundationEnvelope.from_dict(data_foundation_v1)
# definition 必须是完整的 FactorDefinition;FactorSetRef.create 还要求显式 input bindings、
# view availability、output quality/coverage、canonical output bytes 和 immutable artifact ref。
factor_set = FactorSetRef.create(
definitions=(definition,),
dataset_snapshot=snapshot,
foundation=foundation,
**explicit_factor_set_evidence,
)
availability_mode="as_available" 声明 source/view 和计算产物在历史 evaluation 前实际可用;
"retrospective_replay" 保留历史 evaluation,但要求真实 publication/view creation、compute 和
artifact 时间位于之后,并固定 historical_availability="not_established"。两种模式都不会授予
decision、real-data、production、paper 或 live readiness。
旧 governed_pipeline.FactorVersion 的四字段构造器、factor_id@version、run/target/risk/order
identity 均保持不变。迁移只能通过 content-addressed LegacyFactorBinding,再显式调用
bind_legacy_factor() 或 project_legacy_factor();后者是有损投影,不表示旧 digest 与新定义
digest 等价,也不会把旧 run 静默升级为新合同。
回测引用与证据合同 v1
quant_engine.governed_pipeline.BacktestRunRef 是合格回测运行身份的唯一权威。run_id 只由
已验收的 Dataset Snapshot / Data Foundation / FactorSetRef 身份、universe、日历与公司行动
祖先、策略、执行/成本模型、严格整数 seed、完整代码提交、环境锁、配置、时间和重放血缘决定;
它不包含任何输出摘要。重放必须绑定直接父运行、连续 attempt 和不变的
replay_spec_digest,输入漂移或血缘环会失败关闭。
quant_engine.artifact.BacktestEvidenceManifest 只摘要 ResearchRunArtifact 已有的九张事实表。
固定 offline_research_v1 映射为 run、signal、fill、position_nav、performance、
attribution、risk_snapshot 与 replay;每张表都保留列模式摘要、行数和内容摘要,空 risk
表也必须有稳定 schema。manifest_id 由完整 RunRef 与输出证据决定,因此结果变化不会反向改变
run_id。当前 artifact 不拥有订单或拒绝事实,所以此画像明确不声明 order / rejection。
旧 BacktestRun 只能通过 build_legacy_backtest_evidence_manifest() 显式映射为
LEGACY_EXPLORATORY;不能隐式提升为 CONTRACT_QUALIFIED。所有资格均只描述离线证据闭合,
不表示投资有效、组合获批、Paper、生产或实盘就绪。
组合决策与风险评估合同 v1
quant_engine.portfolio_risk_contracts 是现有计算 owner 外围的薄合同层。创建
PortfolioDecision 必须同时提供完整 BacktestRunRef、嵌入同一 RunRef 的
CONTRACT_QUALIFIED 非 legacy BacktestEvidenceManifest、现有 PortfolioTarget、
FreshnessPolicy、ConstraintSetV1 与 ComputationReceipt。适配器会从权威输入独立重算
receipt 的 input/constraint/output digest、敞口、持仓数和 L1 turnover 残差;receipt 自报
成功、fallback 或放宽 tolerance 均不能替代复核。
from quant_engine.portfolio_risk_contracts import (
ComputationReceipt,
ConstraintSetV1,
FreshnessPolicy,
assess_portfolio_risk,
build_portfolio_decision,
compute_portfolio_receipt_digests,
)
freshness = FreshnessPolicy(
max_manifest_age_seconds=3600,
max_covariance_age_days=5,
)
constraints = ConstraintSetV1(
gross_exposure_max=1.0,
single_asset_max=0.10,
position_count_max=20,
turnover_max=0.30,
)
# 生产者先形成公开 canonical digest;decision 构建时仍会独立重算。
expected = compute_portfolio_receipt_digests(
backtest_run_ref=run_ref,
manifest=evidence_manifest,
target=portfolio_target,
objective_name="long_only_allocation",
objective_version="1.0.0",
objective_digest=objective_digest,
model_name="factor_weighting",
model_version="1.0.0",
model_digest=model_digest,
expected_return_digest=expected_return_digest,
covariance_digest=covariance_digest,
scenario_digest=scenario_digest,
constraints=constraints,
freshness_policy=freshness,
prior_weights=prior_weights,
)
receipt = ComputationReceipt(
algorithm="factor_weighting",
algorithm_version="1.0.0",
implementation_digest=implementation_digest,
parameter_digest=parameter_digest,
input_digest=expected["input_digest"],
constraint_digest=expected["constraint_digest"],
output_digest=expected["output_digest"],
status="completed",
solver_required=False,
solver_name=None,
solver_version=None,
solver_config_digest=None,
iterations=None,
objective_value=None,
max_constraint_residual=expected["max_constraint_residual"],
tolerance=1e-12,
computed_at=computed_at,
)
decision = build_portfolio_decision(
backtest_run_ref=run_ref,
manifest=evidence_manifest,
target=portfolio_target,
objective_name="long_only_allocation",
objective_version="1.0.0",
objective_digest=objective_digest,
model_name="factor_weighting",
model_version="1.0.0",
model_digest=model_digest,
expected_return_digest=expected_return_digest,
covariance_digest=covariance_digest,
scenario_digest=scenario_digest,
constraints=constraints,
freshness_policy=freshness,
receipt=receipt,
computed_at=computed_at,
prior_weights=prior_weights,
)
assessment = assess_portfolio_risk(
portfolio_decision=decision,
backtest_run_ref=run_ref,
manifest=evidence_manifest,
covariance=covariance_snapshot,
risk_model_name="euler_volatility",
risk_model_version="1.0.0",
risk_model_digest=risk_model_digest,
)
source_universe_digest 保留 S3 研究 universe 身份,portfolio_asset_set_digest 只描述实际
目标资产标签;二者不会互相冒充成员证明。风险评估在任何数值计算前要求 covariance、target、
RunRef 的 dataset identity 三方一致,并且只调用一次现有 labeled_component_risk()。合同中的
qualified 仅表示 S4.1 计算证据闭合,不授予 maker-checker、发布、订单、Paper、生产或实盘权限。
治理垂直切片
governed_pipeline 不复制因子、回测、组合或执行算法,只编排现有能力并补充版本与风险契约。
调用方必须显式提供 DatasetSnapshot、FactorVersion、StrategyVersion、代码提交和
RiskPolicy。模块只会生成 environment="paper" 的订单意图,不连接数据库、数据供应商或
券商;风险决策为拒绝时,订单意图固定为空,直接调用创建函数也会失败关闭。
该切片对应 ResearchHub 架构的首个可执行验收链路:
DatasetSnapshot → FactorVersion → StrategyVersion → BacktestRun
→ PortfolioTarget → RiskDecision → PaperOrderIntent
平台总架构、五仓职责和十二层能力映射仍以 research_platform/docs/architecture/ 为权威;
本仓只拥有纯计算与离线模拟合同。
与 research_results 的关系
research_results 依赖 quant_engine(通过 re-export 保持向后兼容):
# research_results/src/shared/alpha_factors.py 现在是:
from quant_engine.alpha_factors import * # re-export
47 个 proj 的 import 路径暂时不变(from src.shared.alpha_factors import ... 仍可用)——后续逐步迁移到 from quant_engine.alpha_factors import ...。