1911 字
10 分钟
《量化交易入门》第 5 章:回测——让历史告诉你答案

《量化交易入门》第 5 章:回测——让历史告诉你答案#

上一章我们手里有了一个完整策略,但它到底灵不灵?总不能先拿真金白银试。这一章给你一台”时光机”:把策略放回过去的数据里跑一遍,看看如果当初严格照做,今天会是什么结果。这就是回测——量化里最便宜、也最容易被自己骗的一步。

5.1 回测是什么#

回测(Backtest)= 假设自己带着策略回到过去,模拟做一遍交易,最后算账。

做法朴素:准备一段历史价格,程序从第一天走到最后一天,每一天按策略判断该不该买、该不该卖,虚拟成交,记下账户里有多少钱。走完,你就得到一份”假如过去 60 天我这么干”的成绩单。

它的价值在于便宜:一个策略实盘试错,可能要亏几万、花半年;回测一次,几秒钟,电费几分钱。如果一个策略在过去的数据上都赚不到钱,你基本没有理由相信它未来会突然灵。

但反过来要记住:回测赚钱,不等于实盘赚钱——这一点 5.6 节专门拆。

5.2 回测的四个零件#

任何回测程序,拆开都是这四块:

零件作用
历史数据过去每天的价格(这一章用模拟数据)
信号每天按规则算金叉、死叉
成交模拟信号出现后买入/卖出,扣减现金和持仓
净值曲线每天收盘记下账户总市值

注意”成交”这一步藏着讲究:信号是收盘后才算出来的(要用收盘价),你不可能在同一个收盘价上成交。所以严谨的回测都规定:今天出信号,明天才能成交。这就是下一节代码里 i+1 的含义。

5.3 完整代码:60 天回测#

下面把第 4 章的均线交叉策略放进 60 个交易日里完整回测,初始资金 10 万,金叉次日全仓买入、死叉次日全部卖出。

closes = [
# 第1段:下跌
12.0,11.8,11.6,11.4,11.2,11.0,10.8,10.6,10.4,10.2,
10.0, 9.8, 9.7, 9.6, 9.5, 9.4, 9.5, 9.7,10.0,10.4,
# 上涨
10.8,11.2,11.6,12.0,12.3,12.6,12.8,13.0,12.9,12.7,
# 见顶回落
12.4,12.0,11.6,11.3,11.1,11.0,11.2,11.5,11.8,11.6,
# 第二段震荡
11.3,11.0,10.8,10.7,10.8,11.0,11.3,11.6,11.4,11.1,
10.9,10.8,10.9,11.1,11.4,11.6,11.5,11.3,11.0,10.9,
]
def ma(data, i, w):
return sum(data[i-w+1:i+1]) / w
cash = 100000.0
shares = 0
trades = []
equity_curve = []
prev_diff = None
for i in range(19, len(closes)):
diff = ma(closes, i, 5) - ma(closes, i, 20)
# 今天确认的信号,明天(i+1)才成交——避免未来函数
if prev_diff is not None and i + 1 < len(closes):
if prev_diff <= 0 < diff and shares == 0: # 金叉:次日买入
price = closes[i+1]
shares = cash / price
cash = 0
trades.append(("买入", i+2, price))
elif prev_diff >= 0 > diff and shares > 0: # 死叉:次日卖出
price = closes[i+1]
cash = shares * price
shares = 0
trades.append(("卖出", i+2, price))
equity = cash + shares * closes[i] # 当日收盘总市值
equity_curve.append(equity)
prev_diff = diff
for action, day, price in trades:
print(f"第{day}天 {action},成交价 {price}")
final = equity_curve[-1]
print(f"期末资金: {final:.2f}")
print(f"总收益率: {(final/100000-1)*100:+.2f}%")
peak = equity_curve[0]
max_dd = 0
for e in equity_curve:
peak = max(peak, e)
max_dd = min(max_dd, e/peak - 1)
print(f"最大回撤: {max_dd*100:.2f}%")

运行结果(可复制到本地验证):

第23天 买入,成交价 11.6
第37天 卖出,成交价 11.2
第52天 买入,成交价 10.8
第53天 卖出,成交价 10.9
第58天 买入,成交价 11.3
期末资金: 93996.32
总收益率: -6.00%
最大回撤: -16.13%

5.4 读懂这份成绩单#

别看数字小,每个都在说话:

  • 总收益率 -6%:这个策略在这段行情里是亏的。第一次交易 11.6 买、11.2 卖,期间价格明明冲到过 13——这就是第 4 章说的滞后,等死叉确认时利润全还了回去还倒亏。
  • 最大回撤 -16.13%:账户从最高点最多亏下去 16%。它衡量的是”过程有多难受”——很多人不是策略不行,是扛不住中途这个坑。
  • 交易只有 4 次:均线交叉是低频策略,不会天天折腾。

请注意:这份”不及格”的成绩单,恰恰是回测最大的成功。 我们花几秒钟、零元,就避免了带着 10 万块进市场亏 6000。回测的第一使命是证伪——帮你排除烂策略,而不是证明你聪明。

还有一个容易被忽略的问题:这份成绩单的样本太少了。 60 天里只交易 4 次,4 次里错一次,“胜率”这种数字就会剧烈跳动。现实中,如果你只用四五次交易就判定一个策略行或不行,结论非常脆弱——赶上的可能只是一小段特殊行情。所以评估策略时,除了看收益率,还要问:一共交易了多少次?跨越了多少种市场环境(上涨、下跌、横盘都遇到过吗)?次数越多、经历的环境越全,结论才越可信。 这也是为什么真实的策略研究往往要用几年、甚至十几年的数据,而不是一两个月。

5.5 最致命的坑:未来函数#

新手回测最容易犯、也最隐蔽的错误,叫未来函数(look-ahead bias):在某一天做决定时,用了那天还不可能知道的信息。

比如这一行,如果写成”今天金叉、按今天收盘价买入”,回测会好看很多——因为你等于看着答案下单:收盘瞬间你”知道”了均线、又用同一个收盘价成交。现实中你收盘后才算出信号,最早只能明天买。代码里 closes[i+1] 就是在堵这个洞。

未来函数的可怕之处是:它不会报错,只会给你一份假得很漂亮的成绩单。 凡是回测收益高到离谱,先怀疑有没有偷看未来。

5.6 回测好看,为什么还会亏#

即使堵住未来函数,回测漂亮、实盘亏钱依然常见,主要有三个原因,后面章节会逐个解决:

  1. 市场会变:过去有效的规律可能失效,趋势行情里测出来的策略,赶上长期横盘就废(第 4 章的震荡打脸)。
  2. 没算成本:手续费、滑点每次看着小,几十次交易累积起来能吃掉全部利润——第 9 章专门算这笔账。
  3. 过度调参:为了贴合历史反复改参数,策略成了”背诵历史”,一遇新行情就露馅,这叫过拟合,第 11 章细讲。

所以看回测,多问一句:这个成绩是策略抓住了规律,还是只是把历史背熟了?

5.7 这一章你该记住的#

  1. 回测 = 带策略回历史数据模拟交易,核心价值是便宜地证伪
  2. 四个零件:数据、信号、成交、净值曲线;今天出信号、明天才成交
  3. 必看指标:总收益率、最大回撤、交易次数
  4. 未来函数不报错但最致命;回测好 ≠ 实盘赚,成本和过拟合后面拆

有了回测这台时光机,你就有了检验任何想法的能力。下一章我们离开均线,去认识量化里真正的”原材料”——常见因子:动量、波动与量价,看看除了交叉,还有哪些规律能变成信号。


📚 《量化交易入门》系列:零基础小白向量化交易,每章一个主题 + 可运行 Python 示例。第 5 章已读,下一章《常见因子:动量、波动与量价》明天见。

《量化交易入门》第 5 章:回测——让历史告诉你答案
https://021028.xyz/posts/default/135/
作者
021028
发布于
2026-10-09
许可协议
CC BY-NC-SA 4.0