This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
- `data_adapter` — 桥接 qtdb_pro 长表与新模块(rename / long-wide / 复权 / vwap 代理)
|
- `data_adapter` — 桥接 qtdb_pro 长表与新模块(rename / long-wide / 复权 / vwap 代理)
|
||||||
- `backtest` — weight-based 多日仿真(rebalance_table / compute_nav / compare_to_benchmark)
|
- `backtest` — weight-based 多日仿真(rebalance_table / compute_nav / compare_to_benchmark)
|
||||||
- `portfolio_construction` — 多期因子分数 → Top-K → 等权目标权重表
|
- `portfolio_construction` — 多期因子分数 → Top-K → 等权目标权重表
|
||||||
|
- `research_pipeline` — 因子日 → 下一真实交易日 → 显式执行价 → 执行审计(防前视编排)
|
||||||
- `metrics` — 绩效(年化收益 / 波动率 / Sharpe / 最大回撤 / Calmar)
|
- `metrics` — 绩效(年化收益 / 波动率 / Sharpe / 最大回撤 / Calmar)
|
||||||
- `factor_library` — 通用方法(turnover / winsorize / IC / OLS / jb_test)
|
- `factor_library` — 通用方法(turnover / winsorize / IC / OLS / jb_test)
|
||||||
- `portfolio_decomp` — 组合分解(risk_parity / mean_variance / 因子归因)
|
- `portfolio_decomp` — 组合分解(risk_parity / mean_variance / 因子归因)
|
||||||
@@ -61,8 +62,8 @@ from quant_engine.alpha_factors import alpha_001, alpha_005, ALPHA158_REGISTRY
|
|||||||
from quant_engine.execution import (
|
from quant_engine.execution import (
|
||||||
ExecutionConfig, simulate_multi_day_with_audit, simulate_with_daily_data,
|
ExecutionConfig, simulate_multi_day_with_audit, simulate_with_daily_data,
|
||||||
)
|
)
|
||||||
|
from quant_engine.research_pipeline import run_factor_execution_research
|
||||||
from quant_engine.backtest import run_weight_backtest
|
from quant_engine.backtest import run_weight_backtest
|
||||||
from quant_engine.portfolio_construction import scores_to_weight_table
|
|
||||||
from quant_engine.indicators import macd, bollinger, kdj
|
from quant_engine.indicators import macd, bollinger, kdj
|
||||||
from quant_engine.data_adapter import (
|
from quant_engine.data_adapter import (
|
||||||
long_to_wide, wide_to_long, rename_tushare_columns,
|
long_to_wide, wide_to_long, rename_tushare_columns,
|
||||||
@@ -73,8 +74,9 @@ from quant_engine.data_adapter import (
|
|||||||
|
|
||||||
# 端到端:qtdb_pro 长表 → 适配 → alpha158 → execution
|
# 端到端:qtdb_pro 长表 → 适配 → alpha158 → execution
|
||||||
df = load_qtdb_daily(["000001.SZ"], "2024-01-01", with_adj=True)
|
df = load_qtdb_daily(["000001.SZ"], "2024-01-01", with_adj=True)
|
||||||
prices, volumes = prepare_execution_inputs(df)
|
close_prices, volumes = prepare_execution_inputs(df)
|
||||||
result = simulate_with_daily_data(prices, initial_cash=1_000_000.0)
|
open_prices, _ = prepare_execution_inputs(df, price_col="open")
|
||||||
|
result = simulate_with_daily_data(close_prices, initial_cash=1_000_000.0)
|
||||||
|
|
||||||
# 已正确滞后的目标权重 → 现金约束执行 → 唯一来源的成交/拒绝/日末持仓/NAV
|
# 已正确滞后的目标权重 → 现金约束执行 → 唯一来源的成交/拒绝/日末持仓/NAV
|
||||||
execution = simulate_multi_day_with_audit(
|
execution = simulate_multi_day_with_audit(
|
||||||
@@ -92,18 +94,25 @@ execution = simulate_multi_day_with_audit(
|
|||||||
print(execution.nav_series)
|
print(execution.nav_series)
|
||||||
print(execution.daily_executions)
|
print(execution.daily_executions)
|
||||||
|
|
||||||
# 多期因子分数 → Top-K 等权组合 → 稳定回测结果
|
# 多期因子分数(必须是 point-in-time 数据)→ Top-K → 下一交易日 open 执行
|
||||||
rebalance_weights = scores_to_weight_table(
|
factor_execution = run_factor_execution_research(
|
||||||
factor_scores,
|
factor_scores,
|
||||||
top_k=20,
|
top_k=20,
|
||||||
gross_exposure=1.0,
|
execution_prices=open_prices,
|
||||||
|
execution_price_field="open",
|
||||||
|
initial_cash=1_000_000.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# run_weight_backtest 是低层算子:只接受收益区间开始前已经生效的持仓权重。
|
||||||
|
# 不要把 signal-date 的 factor_scores/decision_weights 直接传给它。
|
||||||
backtest = run_weight_backtest(
|
backtest = run_weight_backtest(
|
||||||
weights=rebalance_weights,
|
weights=effective_holding_weights,
|
||||||
stock_returns=daily_returns,
|
stock_returns=daily_returns,
|
||||||
initial_capital=1_000_000.0,
|
initial_capital=1_000_000.0,
|
||||||
benchmark_nav=benchmark_nav,
|
benchmark_nav=benchmark_nav,
|
||||||
)
|
)
|
||||||
|
print(factor_execution.schedule.signal_to_execution)
|
||||||
|
print(factor_execution.execution.daily_executions)
|
||||||
print(backtest.stats())
|
print(backtest.stats())
|
||||||
print(backtest.benchmark_report())
|
print(backtest.benchmark_report())
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -76,8 +76,9 @@ def compute_nav_from_weights(
|
|||||||
) -> pd.Series:
|
) -> pd.Series:
|
||||||
"""从调仓表(日期 × 股票权重)+ 个股日收益 → 净值曲线。
|
"""从调仓表(日期 × 股票权重)+ 个股日收益 → 净值曲线。
|
||||||
|
|
||||||
假设:在调仓日之间权重不变(**前向填充**)。
|
假设:输入是该收益测量区间开始前已经生效的持仓权重,并在调仓日之间
|
||||||
调仓日的权重 = `weights.loc[rebalance_date]`。
|
保持不变(**前向填充**)。本函数不会把信号日自动解释为执行日;因子分数
|
||||||
|
应先经交易日历调度和实际执行时点处理,避免把同一时点未知的收益计入。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
weights: 调仓日 × 股票代码 的权重 DataFrame(**0~1**,行和 ≤ 1)
|
weights: 调仓日 × 股票代码 的权重 DataFrame(**0~1**,行和 ≤ 1)
|
||||||
|
|||||||
@@ -91,10 +91,10 @@ def test_scores_to_weight_table_constructs_each_rebalance_independently() -> Non
|
|||||||
pd.testing.assert_series_equal(result.iloc[0], changed_result.iloc[0])
|
pd.testing.assert_series_equal(result.iloc[0], changed_result.iloc[0])
|
||||||
|
|
||||||
|
|
||||||
def test_factor_scores_flow_directly_into_weight_backtest() -> None:
|
def test_effective_holding_weights_flow_into_weight_backtest() -> None:
|
||||||
dates = pd.date_range("2026-01-05", periods=3, freq="B")
|
dates = pd.date_range("2026-01-05", periods=3, freq="B")
|
||||||
scores = pd.DataFrame(
|
effective_weights = pd.DataFrame(
|
||||||
{"A": [2.0, 0.0], "B": [1.0, 3.0]},
|
{"A": [1.0, 0.0], "B": [0.0, 1.0]},
|
||||||
index=dates[[0, 2]],
|
index=dates[[0, 2]],
|
||||||
)
|
)
|
||||||
stock_returns = pd.DataFrame(
|
stock_returns = pd.DataFrame(
|
||||||
@@ -102,11 +102,10 @@ def test_factor_scores_flow_directly_into_weight_backtest() -> None:
|
|||||||
index=dates,
|
index=dates,
|
||||||
)
|
)
|
||||||
|
|
||||||
weights = scores_to_weight_table(scores, top_k=1)
|
result = run_weight_backtest(effective_weights, stock_returns)
|
||||||
result = run_weight_backtest(weights, stock_returns)
|
|
||||||
|
|
||||||
pd.testing.assert_series_equal(result.nav, pd.Series([1.1, 1.1, 1.32], index=dates))
|
pd.testing.assert_series_equal(result.nav, pd.Series([1.1, 1.1, 1.32], index=dates))
|
||||||
pd.testing.assert_frame_equal(result.weights, weights)
|
pd.testing.assert_frame_equal(result.weights, effective_weights)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("top_k", [0, -1])
|
@pytest.mark.parametrize("top_k", [0, -1])
|
||||||
|
|||||||
Reference in New Issue
Block a user