From a4212785279f0684dd6f0a34d009321bd3644912 Mon Sep 17 00:00:00 2001 From: ao gong <41768719+ageorge156@users.noreply.github.com> Date: Fri, 21 Aug 2026 22:05:28 +0800 Subject: [PATCH] fix: align ledger performance with signal window --- README.md | 29 +++++++++++++++++++++++---- src/quant_engine/execution.py | 1 + src/quant_engine/research_pipeline.py | 19 +++++++++++++----- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 93d3103..d16bdbd 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,12 @@ ## 模块 - `alpha_factors` — 158 alpha 公式 + 24 基础算子(移植自 qlib alpha158) -- `execution` — A 股长仓执行仿真(成本/滑点/现金约束)+ 逐日成交/拒绝/持仓/NAV 审计;T+1、涨跌停、成交量与价差提供独立约束函数 +- `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` — 因子日 → 下一真实交易日 → 显式执行价 → 执行审计(防前视编排) +- `research_pipeline` — 因子日 → 下一真实交易日 → 显式执行价 → 日末估值 → 成本后绩效(防前视编排) - `metrics` — 绩效(年化收益 / 波动率 / Sharpe / 最大回撤 / Calmar) - `factor_library` — 通用方法(turnover / winsorize / IC / OLS / jb_test) - `portfolio_decomp` — 组合分解(risk_parity / mean_variance / 因子归因) @@ -60,9 +60,12 @@ ruff check src/ tests/ # lint ```python from quant_engine.alpha_factors import alpha_001, alpha_005, ALPHA158_REGISTRY from quant_engine.execution import ( - ExecutionConfig, simulate_multi_day_with_audit, simulate_with_daily_data, + 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.research_pipeline import 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 ( @@ -103,6 +106,24 @@ factor_execution = run_factor_execution_research( 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) + # run_weight_backtest 是低层算子:只接受收益区间开始前已经生效的持仓权重。 # 不要把 signal-date 的 factor_scores/decision_weights 直接传给它。 backtest = run_weight_backtest( diff --git a/src/quant_engine/execution.py b/src/quant_engine/execution.py index 5d38ec3..9fd431e 100644 --- a/src/quant_engine/execution.py +++ b/src/quant_engine/execution.py @@ -10,6 +10,7 @@ 借鉴 hikyuu SG/MM/CN/PG 部件化思想(不引入 hikyuu 框架): - ExecutionConfig:佣金 + 印花税 + 滑点 + 最小交易额 + 止损/止盈阈值 - simulate_execution():从目标权重 → 实际成交金额(应用成本/滑点) +- simulate_daily_ledger_with_audit():稀疏调仓 + 完整交易日收盘估值 Ledger - simulate_multi_day_with_audit():目标权重差额调仓(成交/拒绝/持仓/NAV) - simulate_multi_day():兼容的多日日末持仓快照入口 - check_stop_loss_take_profit():止损/止盈触发判定 diff --git a/src/quant_engine/research_pipeline.py b/src/quant_engine/research_pipeline.py index b1ee3fa..2b52e9d 100644 --- a/src/quant_engine/research_pipeline.py +++ b/src/quant_engine/research_pipeline.py @@ -1,8 +1,10 @@ -"""可信研究链路:因子分数经交易日历滞后后进入执行审计。 +"""可信研究链路:因子分数经交易日历滞后后进入执行与日频 Ledger。 本模块只编排现有组合构建与执行组件,不连接账户、券商或实盘订单。 时间契约借鉴 Qlib 的 prediction/trade time 分离与 Backtrader 的 next-bar 执行语义:signal_date 上形成的目标权重,默认最早在下一交易时点执行。 +完整回测链路进一步分离 execution price 与日末 valuation price,非调仓日也 +持续盯市,并从真实成交后持仓派生日收益和绩效。 """ from __future__ import annotations @@ -300,6 +302,13 @@ def run_factor_backtest_research( execution_calendar, lag_sessions=lag_sessions, ) + if decision_weights.empty: + execution_window = execution_snapshot.iloc[:0].copy() + valuation_window = valuation_snapshot.iloc[:0].copy() + else: + research_start = decision_weights.index[0] + execution_window = execution_snapshot.loc[research_start:].copy() + valuation_window = valuation_snapshot.loc[research_start:].copy() target_history: list[tuple[str, dict[str, float]]] = [] execution_history: list[tuple[str, dict[str, float]]] = [] @@ -308,7 +317,7 @@ def run_factor_backtest_research( target_history.append( (date_label, {asset: float(weight) for asset, weight in weights.items()}) ) - prices = execution_snapshot.loc[execution_date] + prices = execution_window.loc[execution_date] execution_history.append( (date_label, {asset: float(price) for asset, price in prices.items()}) ) @@ -318,7 +327,7 @@ def run_factor_backtest_research( str(pd.Timestamp(valuation_date)), {asset: float(price) for asset, price in prices.items()}, ) - for valuation_date, prices in valuation_snapshot.iterrows() + for valuation_date, prices in valuation_window.iterrows() ] execution = simulate_daily_ledger_with_audit( target_history, @@ -329,8 +338,8 @@ def run_factor_backtest_research( ) return FactorBacktestResult( factor_scores=factor_snapshot, - execution_prices=execution_snapshot, - valuation_prices=valuation_snapshot, + execution_prices=execution_window, + valuation_prices=valuation_window, schedule=schedule, execution_price_field=execution_field, valuation_price_field=valuation_field,