feat: 量化引擎加固 — 新增测试 + 数据/因子/回测层优化

- 新增 finance/tests/ 6 个测试套件(agents/backtest/dao_upsert/factors/features/fundamental_lookahead)
- 数据层: data_manager / dao 优化,新增 upsert 逻辑
- 因子层: 基本面因子抽象定位 _mapping、ROE/PE/PB 重构
- 回测层: vectorbt/engine 大改动(251 行),report 增强
- ML 层: features/backtest_integration 特征工程与回测优化
- CLI: agent_cli 重构
- config/settings 扩充配置项
This commit is contained in:
Simon
2026-08-31 14:01:06 +08:00
parent 6acf938caf
commit 73d191b43a
28 changed files with 1418 additions and 373 deletions
@@ -0,0 +1,71 @@
"""
基本面因子前视回归测试:财报不得在披露日之前被回测看到。
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import numpy as np
import pandas as pd
import pytest
from factors.fundamental.pe_pb import PEFactor
from factors.fundamental.roe import ROEFactor, ROETTMDeltaFactor
from factors.fundamental._mapping import map_fundamental_to_daily, effective_available_dates
INDICES = pd.date_range('2024-01-01', '2024-09-30', freq='B').strftime('%Y%m%d')
def _daily():
return pd.DataFrame({'close': np.full(len(INDICES), 10.0)}, index=INDICES)
def _fin_ann():
# 提供 ann_date(实际披露日):年报 4/25, 一季报 4/28, 中报 8/30
return pd.DataFrame({
'end_date': ['20231231', '20240331', '20240630'],
'ann_date': ['20240425', '20240428', '20240830'],
'eps': [1.0, 1.2, 1.5],
'roe': [0.10, 0.12, 0.15],
})
def test_lookahead_absent_before_disclosure():
daily = _daily()
mapped = map_fundamental_to_daily(daily, _fin_ann(), 'eps')
# 披露日(4/25)之前绝不可见任何财务值 → 消除前视
pre = mapped[mapped.index <= '20240424'].dropna()
assert pre.empty, "披露日前出现财务值 → 前视泄漏!"
# 4/25 起可见年报 eps=1.0
post = mapped[mapped.index >= '20240425'].dropna()
assert abs(float(post.iloc[0]) - 1.0) < 1e-6
def test_ann_date_refines_overlap():
daily = _daily()
mapped = map_fundamental_to_daily(daily, _fin_ann(), 'eps')
# 4/25-4/27 是年报(1.0),4/28 起切换为一季报(1.2)
band = mapped[(mapped.index >= '20240425') & (mapped.index <= '20240427')]
assert (band.dropna() == 1.0).all()
post = mapped[mapped.index >= '20240428'].dropna()
assert abs(float(post.iloc[0]) - 1.2) < 1e-6
def test_pe_roe_no_lookahead():
daily = _daily()
fin = _fin_ann()
for f in [PEFactor(fin), ROEFactor(fin), ROETTMDeltaFactor(fin)]:
s = f.calculate(daily)
pre = s[s.index <= '20240424'].dropna()
assert pre.empty, f"{f.name} 在披露日前出现值 → 前视泄漏"
def test_default_disclosure_lag_without_ann_date():
"""无 ann_date 时用法定滞后:年报(1231)→次年4/30。"""
daily = _daily()
fin = pd.DataFrame({'end_date': ['20231231'], 'eps': [1.0]})
mapped = map_fundamental_to_daily(daily, fin, 'eps')
pre = mapped[mapped.index <= '20240429'].dropna()
assert pre.empty, "无 ann_date 时财报不应在披露日前可见"
post = mapped[mapped.index >= '20240430'].dropna()
assert not post.empty