STAGE 9 仓位管理与资金管理 ← 返回笔记
投资笔记 约 18 分钟阅读 阶段 9 · 第 13 篇

量化回测框架与 Python 工具入门:避开 6 大致命陷阱,让策略在数据中现原形

主观判断最大的问题,是选择性记忆——亏了觉得无关紧要,赚了牢牢记住。回测的价值不是"证明你很牛",而是在数据面前撕掉你的过度自信。但 90% 的散户量化尝试都死在了同样的六个坑里:未来函数、幸存者偏差、过度拟合、忽略交易成本、数据窥探、复权方式错误。

一句话核心

回测不是"策略赚钱能力的证明",而是"策略是否经得起数据拷问的压力测试"。一条健康的回测曲线要同时满足:参数 ≤ 5 个、跨周期验证、扣除全部交易成本、最大回撤 < 20%、夏普比率 > 1.5。做不到这五条的策略,无论看着多么漂亮,都只是未来函数 + 幸存者偏差 + 过度拟合合谋制造的虚假幻觉。

为什么需要量化回测:撕掉主观记忆的滤镜

你有没有这样的体验:账户上个月明明赚了 8000 块,你却觉得"好像没赚多少";可上上个月亏了 2000 块,你却记得清清楚楚,连哪只票、哪一天买的都能复述。这不是记忆力问题,是人类大脑天然的选择性记忆偏差。

这种偏差在投资里的表现非常一致:

量化回测的价值,就是让冷冰冰的数据替代你的记忆。它能在几秒钟之内回答三个核心问题:

回测解决的不是"选哪个策略"的问题,而是"在你以为有效的策略里,先把不靠谱的快速剔除掉"的问题。一个无法在历史数据里站住脚的策略,到了实盘一定死得更难看——因为实盘还有滑点、情绪、黑天鹅三个回测里都没出现的额外打击。

但要小心:回测同时也是一个最容易自欺欺人的工具。用得不对,它会让你比主观判断更危险——因为你会在虚假的安全感里加大注码。我们接着看那 6 大致命陷阱。

回测的 6 大致命陷阱

这 6 个坑,90% 的初学者都会全部踩一遍。它们不是技术 bug,而是认知陷阱——你越聪明,越容易踩得深。下面一个一个拆。

陷阱 1:未来函数(Look-ahead Bias)

这是回测里最常见、也最隐蔽的错误:在决策时偷看了还没发生的数据。

未来函数的典型场景

错误做法为什么错正确做法
用当日收盘价算信号收盘价 15:00 才确定,你不可能在当天买入次日开盘买入
用当日最高/最低价决定买卖日内的高低点是事后才知道的次日开盘价成交
回看 20 天后才公告的财报公告日之后你才能看到按公告日 T+1 决策

未来函数会让你的回测曲线看起来无比漂亮——但每漂亮一分,实盘就会难看十分。原因很简单:实盘里你永远拿不到还没发生的数据,而回测里你偷看了,结果差距就是几十个百分点。

陷阱 2:幸存者偏差(Survivorship Bias)

用今天还活着的成分股,去回测过去 10 年——这等于自动忽略了所有已经退市的股票。而退市的票里恰恰藏着最多的暴跌、停牌、归零。

陷阱 3:过度拟合(Overfitting)

当你用 50 个参数 + 1000 只股票 + 10 年日线 去"找规律",你几乎一定能在历史数据里"挖"出一个年化 80% 的"圣杯"。但这个圣杯只对过去有效,对未来完全无效。

参数自由度过高 → 在噪声中拟合出"规律"
机器学习里叫"奥卡姆剃刀":解释力相同的模型,越简单越好。参数越多,过拟合概率指数级上升。

正确做法只有两条:参数尽量少(< 5 个),样本外验证(Out-of-Sample Testing)。比如用 2010—2018 训练,用 2019—2024 验证,这是最朴素但最有效的方法。

陷阱 4:交易成本忽略

这是新手回测里最普遍、最容易让人自我感觉良好的陷阱。一组直观数字:

双向交易成本 ≈ 0.05% + 0.025% + 0.1% = 0.175%
印花税(卖出)0.05% + 佣金 0.025%(双边) + 滑点 0.1% → 全周期双边成本约 0.175%

听起来不多?算给你看:

高频策略忽略交易成本 = 稳亏不赚的死亡螺旋。任何回测都必须扣除:印花税 0.05%(卖出单边)+ 佣金万 2.5(双边)+ 滑点至少 0.1%。

陷阱 5:数据质量 / 数据窥探(Data Snooping)

在同一份历史数据上反复测试、反复调参、反复"找规律"——哪怕一开始没有规律,你测试 1000 次也能凑巧出 50 个"看似有效"的策略。这就是数据窥探。

陷阱 6:复权方式选择错误

A 股有除权除息,需要对历史价格做调整。但复权方式选错,会直接污染你的回测:

三种复权方式的对比

复权方式特点使用场景
不复权原始价格,含跳空缺口仅用于查看真实历史成交
前复权以最新价为基准回溯调整实盘看盘(推荐)
后复权保持早期数据连续性回测专用(推荐)

如果用前复权做回测,早期数据会随着每次分红送股被重新调整,长期看价格曲线会出现不真实的"塌陷"。回测用后复权,实盘用前复权,这是不可违反的铁律。

回测框架的核心组件:数据 / 信号 / 持仓 / 评估

一个完整的回测框架,本质上由四层组成:数据层 → 信号层 → 持仓层 → 业绩评估。下面分别拆解每一层的关键设计要点。

1. 数据层:决定回测真实性的根基

回测结果的可信度,80% 取决于数据质量。数据层要解决三件事:

缺失复权因子的回测都是耍流氓。不复权 ≠ 真实价格,会导致分红送股后的跳空被误判为"暴跌信号"。这一点几乎是 90% 散户回测犯的第一个错。

2. 信号层:策略灵魂的数字化表达

信号层负责把"我觉得要涨"翻译成机器能执行的规则。常见三类信号:

信号类型分类

类型典型例子说明
阈值型MA5 > MA20 买入规则最简单、最容易验证
形态型早晨之星、MACD 金叉多条件组合,过拟合高发区
组合型量价共振 + 趋势 + 估值多因子打分,参数容易失控

无论哪种信号,生成时机必须是"当日收盘 + 第二日开盘"或者更晚。这条规则再强调一次,因为它直接对应"未来函数"陷阱 1。

3. 持仓层:资金分配与再平衡

信号告诉你买什么,持仓层决定买多少。健康的资金分配规则:

4. 业绩评估:7 个健康指标

回测结束不代表结束——评估才是策略能否上实盘的最后一关。下面是 7 个核心指标的"健康值"参考:

回测业绩健康指标

指标含义健康值
年化收益复合年化收益率> 15%
最大回撤从峰值到谷底的最大跌幅< 20%
夏普比率(收益 − 无风险)/ 波动率> 1.5
卡玛比率年化收益 / 最大回撤> 1.0
胜率盈利交易占比> 50%
盈亏比平均盈利 / 平均亏损> 1.5
换手率资金周转速度越低越好
注意:年化 15% 听起来不高,但叠加最大回撤 < 20%、夏普 > 1.5这两个约束条件,能同时满足的策略在 A 股长期看已经是 TOP 5% 的水平。任何看着年化 50%+ 的策略,几乎都可以判定为存在陷阱 1—3 中的某一个。

Python 工具推荐与双均线示例代码

Python 是量化回测的事实标准。下面按"数据获取 → 回测框架"两个维度推荐工具,并给出一段可直接运行的双均线示例。

1. 数据获取工具

A 股常用数据源对比

工具费用数据范围适合人群
akshare完全免费A 股 / 港股 / 美股 / 基金 / 期货新手首选,无注册门槛
baostock完全免费A 股证券数据齐全需要完整复权因子的用户
tushare pro积分制(基础免费)A 股全量数据需要稳定高频数据的进阶用户
wind / iFinD机构付费全市场 + 宏观 + 衍生数据专业机构 / 量化团队

2. 回测框架

主流 Python 回测框架对比

框架特点适合场景
backtrader经典回测框架,社区庞大事件驱动策略、多标的组合
vectorbt向量化回测,性能极强参数扫描 / 大规模回测
qstock小而美,开箱即用轻量级个人策略验证
zipline / quantstats专业量化平台级研究型 / 机构级回测

3. 双均线策略示例代码

下面是一段可直接复制运行的最小回测示例,用 akshare 拉数据、用 pandas 做双均线策略、用 shift(1) 避免未来函数:

Python · 双均线策略最小回测
import pandas as pd
import akshare as ak

# 1. 获取数据(后复权,避免陷阱 6)
df = ak.stock_zh_a_hist(symbol="000001", period="daily", adjust="hfq")

# 2. 计算均线
df['MA5']  = df['收盘'].rolling(5).mean()
df['MA20'] = df['收盘'].rolling(20).mean()

# 3. 生成信号(金叉买入 / 死叉卖出)
df['signal'] = 0
df.loc[df['MA5'] > df['MA20'], 'signal'] = 1
df.loc[df['MA5'] < df['MA20'], 'signal'] = -1

# 4. 计算收益(shift(1) 避免未来函数,对应陷阱 1)
df['ret']          = df['收盘'].pct_change()
df['strategy_ret'] = df['signal'].shift(1) * df['ret']

# 5. 评估业绩(参考 7 个健康指标)
annual_ret = df['strategy_ret'].mean() * 252
# 注:上面算的是简单平均年化(单利近似),复合年化要用 (1+ret).prod()**(252/n) - 1
nav         = (1 + df['strategy_ret']).cumprod()
peak        = nav.cummax()
max_dd      = ((nav - peak) / peak).min()
# 注:原代码用 cumsum() 的减法算的是"日收益累加曲线"与峰值之差,量纲错误
print(f"年化收益: {annual_ret:.2%}, 最大回撤: {max_dd:.2%}")
这段代码 30 行不到,但已经内置了所有"防陷阱机制":后复权数据、shift(1) 防止未来函数、滚动计算、收益序列、最大回撤。这是你能写出的最干净的最小回测原型。先在这个基础上跑通,再去套 backtrader 或 vectorbt 才有意义。

回测报告的 5 大禁忌与 3 个常见误区

一份诚实的回测报告,比一份漂亮的回测报告有价值 100 倍。下面先看回测报告的 5 大禁忌——任何一份报告犯了其中之一,请直接判定为不可信。

⚠️ 回测报告的 5 大禁忌

① 只展示胜率(隐藏回撤) · ② 只展示最好年份(隐藏最差年份) · ③ 只展示最优参数(隐藏参数敏感性) · ④ 没有交易次数(10 次和 1000 次完全是两回事) · ⑤ 没有跨周期验证(只在牛市中跑过不算数)。

3 个最常见的认知误区

回测的 3 个致命误区

误区真相
回测赚钱 = 实盘赚钱滑点 + 心理因素 + 流动性,通常会让收益缩水 30%—60%
策略越复杂越好5 个参数 > 50 个参数,简单即正义
胜率高 = 策略好盈亏比同样关键,胜率 40% + 盈亏比 3 的策略远胜胜率 80% + 盈亏比 0.5
回测不是终点
而是策略进入实盘前的最后一次拷问
连回测都站不住的策略
在实盘里一定站不住

本节要点