13-定投回测方法论与结果

基于本地数据库的历史行情与估值分位,对比"简单定投"与"估值分位定投"两种策略的长期收益。回测脚本:backtest_dca.py,数据来源:05-数据库

回测快照时间:2026-07-15 数据库最新交易日:2026-07-14

一、回测方法论

1.1 设计原则

回测遵循 数据库概览 中"回测在终点段 02-定投策略研究 内完成"的边界:

  • 数据库只提供数据:从 index_dailyindex_valuation 表读取行情和 PE 历史分位
  • 回测逻辑在 Python 中执行:扣款、累计份额、计算收益等步骤不进入 SQL
  • 不写入数据库:回测结果仅用于策略选型,不污染行为数据表(decision_log 等留给真实决策)

1.2 策略定义

策略代号扣款规则倍数映射
简单定投simple每月第一个交易日,固定金额始终 1.0x
估值分位定投valuation每月第一个交易日,按当时 PE 历史分位调整金额见下表

估值分位定投的倍数映射,与 06-定投决策查询 中的信号生成逻辑保持一致:

PE 历史分位估值区域定投倍数
[0, 0.2)极低1.5x
[0.2, 0.6)低 / 中1.0x
[0.6, 0.8)0.5x
[0.8, 1.0]极高0x(暂停)

分位缺失的月份按 1.0x 正常定投处理,避免数据缺失导致策略中断。

1.3 回测参数

参数取值说明
扣款时点每月第一个交易日index_daily.trade_date 为准
基础月投金额1000 元估值定投的实际投入 = 基础金额 × 倍数
起始日期2016-07-18数据库中三只标的共同的最早日期
结束日期2026-07-14数据库最新交易日
数据质量过滤data_quality IN ('ok', 'manual_fixed')排除可疑与错误数据
年化口径(累计收益 + 1) ^ (1 / 年数) - 1按自然年化,非 IRR

1.4 回测标的

只选取同时具备完整日线与估值数据、历史深度达 10 年的标的:

指数代码指数名称日线条数估值条数覆盖区间
000016上证50242524252016-07-18 ~ 2026-07-14
000852中证1000242524252016-07-18 ~ 2026-07-14
000905中证500242524252016-07-18 ~ 2026-07-14

未纳入回测的标的及原因

指数原因
000300 沪深300日线数据仅 10 条(fetcher 异常),需重跑 update_all.py 修复
000688 科创50估值数据缺失(2020 年上市,估值 fetcher 未覆盖)
399006 创业板指估值数据缺失(同上)

二、回测结果

2.1 逐标的对比

000016 上证50(2016-07-18 ~ 2026-07-14,121 个月)

策略累计投入(元)期末市值(元)累计收益年化收益暂停月数
简单定投121,000130,609+7.94%+0.77%0
估值定投85,00094,890+11.64%+1.11%34
超额-36,000-35,719+3.70%+0.34%-

000852 中证1000(2016-07-18 ~ 2026-07-14,121 个月)

策略累计投入(元)期末市值(元)累计收益年化收益暂停月数
简单定投121,000149,330+23.41%+2.13%0
估值定投128,500165,867+29.08%+2.60%9
超额+7,500+16,537+5.67%+0.47%-

000905 中证500(2016-07-18 ~ 2026-07-14,121 个月)

策略累计投入(元)期末市值(元)累计收益年化收益暂停月数
简单定投121,000168,627+39.36%+3.39%0
估值定投128,500186,221+44.92%+3.80%15
超额+7,500+17,594+5.56%+0.41%-

2.2 三只标的汇总

指数简单定投年化估值定投年化年化超额累计超额
000016 上证50+0.77%+1.11%+0.34%+3.70%
000852 中证1000+2.13%+2.60%+0.47%+5.67%
000905 中证500+3.39%+3.80%+0.41%+5.56%

三、结论与局限

3.1 结论

  1. 估值分位定投在三只标的上都跑赢简单定投。10 年周期内,累计超额收益 +3.70% ~ +5.67%,年化超额 +0.34% ~ +0.47%。这印证了 00-定投策略总览 中"估值定投、低估加倍、高估暂停"策略的有效性。

  2. 估值定投的优势主要来自"高估暂停"。上证50 的估值定投跳过了 34 个月(占 28%),避开了高估阶段的回撤;中证1000 与中证500 分别跳过 9、15 个月,同时低估加倍又增加了投入。两种机制共同贡献了超额收益。

  3. 不同指数的定投收益差异主要由指数本身表现决定。中证500 > 中证1000 > 上证50 的排序在两种策略下都一致,说明定投策略的 α(超额)建立在指数 β 之上。策略优化不能弥补选错标的的代价。

  4. 绝对收益水平偏低。10 年年化 0.77% ~ 3.80%,与 00-定投策略总览 中 "A 股年化预期 5-10%" 的区间相比偏低,反映了 2016-2026 区间 A 股宽基整体震荡的特征。这也佐证了路线图把纳指100 作为第一大仓位(20%)的配置逻辑。

3.2 局限

局限说明后续改进方向
标的覆盖不足沪深300、科创50、创业板指因数据缺失未纳入修复 fetch_index_daily 后补跑
未考虑交易成本实际定投有申购费、赎回费backtest_dca.py 中增加费率参数
未考虑分红再投index_daily.close 是价格指数,不含分红改用全收益指数或手工补分红
暂停月的资金闲置估值定投暂停月的资金未计入现金储备收益增加现金账户模块,按货币基金收益率计息
未做参数敏感性倍数阈值(0.2/0.6/0.8)固定增加 --low/--high 参数扫描
样本单一仅 10 年 A 股宽基补充海外指数、行业指数回测

3.3 对实盘的启示

  1. 估值定投的纪律性比单次择时更重要。回测中估值定投的优势来自系统性地"高估不买",而不是某一次精准逃顶。实盘中应让倍数映射固化在扣款流程里,避免人工干预。

  2. 暂停月不是"错失机会"而是"等更好的价格"。上证50 的 34 个暂停月看似很多,但最终年化反而更高。实盘中遇到暂停信号时,应按纪律执行,而不是因为"已经涨了"就追进去。

  3. 策略验证需要持续复盘。本次回测是 2016-2026 的历史样本,未来市场结构可能变化。建议每季度用 06-定投决策查询 的查询接口重新跑一次回测,对比"策略预期 vs 实际表现",写入 04-季度复盘

  4. 实盘决策应写入 decision_log。回测结果支撑策略选型,实盘开始后,每次扣款都应通过 07-行为数据查询 写入 decision_log,记录决策时的 PE 分位快照,后续用同一套查询接口做"决策命中率"复盘。

四、如何复现

4.1 前置条件

  1. 数据库已建库并灌入历史数据:
    python 05-数据库/02-数据脚本/db.py
    python 05-数据库/02-数据脚本/update_all.py --mode full
  2. 依赖已安装:pandas(已在 05-数据库/02-数据脚本/requirements.txt 中声明)

4.2 运行回测

# 默认回测 000016/000852/000905,基础月投 1000
python 04-终点段/02-定投策略研究/backtest_dca.py

# 单标的
python 04-终点段/02-定投策略研究/backtest_dca.py --code 000905

# 指定起始日期与月投金额
python 04-终点段/02-定投策略研究/backtest_dca.py --start 2018-01-01 --monthly 2000

4.3 扩展回测

如需增加标的或调整策略参数,修改 backtest_dca.py

  • 增加标的:在 main()default_targets 列表中添加,前提是该指数在数据库中同时有完整日线与估值数据
  • 调整倍数映射:修改 valuation_multiplier() 函数的阈值与返回值
  • 增加止盈逻辑:在 backtest_valuation() 的循环中加入"累计收益超阈值则止盈"分支

相关文档