Files
Simon 73d191b43a 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 扩充配置项
2026-08-31 14:01:06 +08:00

81 lines
2.9 KiB
Python

"""
回测引擎回归测试:T+1 成交、涨跌停拒成交、report DatetimeIndex、截面组合资金切分。
"""
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
vectorbt = pytest.importorskip("vectorbt")
from backtest.vectorbt.engine import VectorBTEngine
from backtest.strategies.momentum_breakout import MomentumBreakoutStrategy
@pytest.fixture
def price_df():
idx = pd.date_range('2024-01-01', '2024-06-30', freq='B').strftime('%Y%m%d')
close = 100 * np.cumprod(1 + np.random.default_rng(42).normal(0.0005, 0.02, len(idx)))
open_p = np.roll(close, 1); open_p[0] = close[0]
pre_close = np.roll(close, 1); pre_close[0] = close[0]
return pd.DataFrame({
'open': open_p, 'close': close, 'pre_close': pre_close,
'pct_chg': (close / pre_close - 1) * 100,
}, index=idx)
def test_backtest_runs_with_t1(price_df):
eng = VectorBTEngine()
rep = eng.run(MomentumBreakoutStrategy(), price_df, t_plus_one=True, limit_check=True, slippage=0.0002)
assert not rep.equity_curve.empty
assert isinstance(rep.equity_curve.index, pd.DatetimeIndex)
def test_limit_up_blocks_buy(price_df):
eng = VectorBTEngine()
p = price_df.copy()
i = p.index[-5]
p.loc[i, 'close'] = p.loc[i, 'pre_close'] * 1.099
p.loc[i, 'pct_chg'] = 9.9
entries = pd.Series(True, index=p.index)
exits = pd.Series(False, index=p.index)
eff, _ = eng._apply_limit_filters(entries, exits, p)
assert not bool(eff.loc[i]), "涨停日应抑制买入"
assert eff.dtype == bool
def test_limit_down_blocks_sell(price_df):
eng = VectorBTEngine()
p = price_df.copy()
i = p.index[-7]
p.loc[i, 'close'] = p.loc[i, 'pre_close'] * 0.901
p.loc[i, 'pct_chg'] = -9.9
_, exf = eng._apply_limit_filters(
pd.Series(False, index=p.index), pd.Series(True, index=p.index), p)
assert not bool(exf.loc[i]), "跌停日应抑制卖出"
def test_normal_day_keeps_entry(price_df):
eng = VectorBTEngine()
p = price_df.copy()
eff, _ = eng._apply_limit_filters(
pd.Series(True, index=p.index), pd.Series(False, index=p.index), p)
assert eff.sum() > 0
def test_cross_section_investable_curve():
idx = pd.date_range('2024-01-01', '2024-06-30', freq='B').strftime('%Y%m%d')
uni = {}
for i in range(4):
c = 50 * np.cumprod(1 + np.random.default_rng(i).normal(0.0003, 0.018, len(idx)))
o = np.roll(c, 1); o[0] = c[0]
uni[f"600{i:04d}.SH"] = pd.DataFrame({'open': o, 'close': c}, index=idx)
eng = VectorBTEngine(initial_capital=100000)
rep = eng.run_cross_section(MomentumBreakoutStrategy(), uni, rebalance_freq="M")
assert not rep.equity_curve.empty
assert isinstance(rep.equity_curve.index, pd.DatetimeIndex)
# 组合净值可联合投资:末值与初始资本量级相当,不会因每股满额而超限
assert rep.equity_curve.iloc[-1] > 0