225 lines
8.9 KiB
Markdown
225 lines
8.9 KiB
Markdown
# 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)
|
||
- `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 订单意图;全链路带确定性 ID,风险拒绝时禁止生成订单意图
|
||
- `artifact` — 版本化、确定性、存储中立的完整 research run 事实表与 manifest
|
||
- `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 等。
|
||
|
||
## 安装
|
||
|
||
```bash
|
||
cd quant_engine
|
||
pip install -e ".[dev]"
|
||
```
|
||
|
||
## 测试
|
||
|
||
```bash
|
||
pytest # 单元测试
|
||
pytest --cov=src # 覆盖率
|
||
mypy --strict src/ # 类型检查
|
||
ruff check src/ tests/ # lint
|
||
|
||
# 无网络、无数据库、无券商的架构烟测
|
||
uv run python -m quant_engine.governed_pipeline
|
||
```
|
||
|
||
## 使用
|
||
|
||
```python
|
||
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())
|
||
```
|
||
|
||
## 治理垂直切片
|
||
|
||
`governed_pipeline` 不复制因子、回测、组合或执行算法,只编排现有能力并补充版本与风险契约。
|
||
调用方必须显式提供 `DatasetSnapshot`、`FactorVersion`、`StrategyVersion`、代码提交和
|
||
`RiskPolicy`。模块只会生成 `environment="paper"` 的订单意图,不连接数据库、数据供应商或
|
||
券商;风险决策为拒绝时,订单意图固定为空,直接调用创建函数也会失败关闭。
|
||
|
||
该切片对应 ResearchHub 架构的首个可执行验收链路:
|
||
|
||
```text
|
||
DatasetSnapshot → FactorVersion → StrategyVersion → BacktestRun
|
||
→ PortfolioTarget → RiskDecision → PaperOrderIntent
|
||
```
|
||
|
||
平台总架构、五仓职责和十二层能力映射仍以 `research_platform/docs/architecture/` 为权威;
|
||
本仓只拥有纯计算与离线模拟合同。
|
||
|
||
## 与 research_results 的关系
|
||
|
||
`research_results` 依赖 `quant_engine`(通过 re-export 保持向后兼容):
|
||
|
||
```python
|
||
# 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 ...`。
|