第四章:策略回测——让数据告诉你,这套策略到底行不行
说实话,写策略最怕什么?不是策略不赚钱,而是你根本不知道它什么时候会亏钱。我见过太多人,拿着一个月的回测曲线就敢上实盘,结果三个月后爆仓离场。嗯,今天我们就来聊聊,怎么用历史数据给偏离度套利策略做个「全身检查」。
4.1 回测框架搭建——别急着写代码,先想清楚你要测什么
我个人习惯,做回测前先画个流程图。你想想看,偏离度套利的核心逻辑其实就三步:
- 计算实时偏离度——正股和可转债的价格差
- 设定阈值触发——偏离度超过±2%时开仓
- 止盈止损管理——偏离度回归到0.5%以内平仓,或者亏损超过1%止损
下面这张图,是我做这个策略时画的流程图,你可以参考一下:
我的小建议:回测周期至少选6个月以上,最好覆盖牛熊转换期。我在2022年做过一次测试,只选了3个月的数据,结果策略表现好得离谱——后来才发现那段时间刚好是单边上涨行情,策略在震荡市里根本没法用。
4.2 核心回测代码——别怕,其实就几十行
很多人一听到「回测」就觉得要写几百行代码。其实不然。偏离度套利的逻辑相对简单,核心代码也就这么一段:
import pandas as pd
import numpy as np
def backtest_deviation_strategy(data, entry_threshold=0.02, exit_threshold=0.005, stop_loss=0.01):
"""
偏离度套利策略回测函数
参数:
data: DataFrame,包含 'deviation' 列(偏离度)
entry_threshold: 开仓阈值,默认2%
exit_threshold: 平仓阈值,默认0.5%
stop_loss: 止损线,默认1%
返回:
trades: 交易记录
metrics: 绩效指标
"""
position = 0 # 持仓状态:0空仓,1多仓,-1空仓
entry_price = 0
trades = []
for i in range(len(data)):
deviation = data.loc[i, 'deviation']
price = data.loc[i, 'price']
timestamp = data.loc[i, 'timestamp']
# 开仓逻辑
if position == 0:
if deviation > entry_threshold:
position = 1 # 做多偏离度
entry_price = price
trades.append({'time': timestamp, 'action': 'buy', 'price': price, 'deviation': deviation})
elif deviation < -entry_threshold:
position = -1 # 做空偏离度
entry_price = price
trades.append({'time': timestamp, 'action': 'sell', 'price': price, 'deviation': deviation})
# 平仓逻辑
elif position == 1:
# 止盈:偏离度回归
if deviation <= exit_threshold:
position = 0
trades.append({'time': timestamp, 'action': 'sell', 'price': price, 'deviation': deviation})
# 止损:亏损超过1%
elif (price - entry_price) / entry_price < -stop_loss:
position = 0
trades.append({'time': timestamp, 'action': 'sell_stop', 'price': price, 'deviation': deviation})
elif position == -1:
# 止盈:偏离度回归
if deviation >= -exit_threshold:
position = 0
trades.append({'time': timestamp, 'action': 'buy', 'price': price, 'deviation': deviation})
# 止损
elif (entry_price - price) / entry_price < -stop_loss:
position = 0
trades.append({'time': timestamp, 'action': 'buy_stop', 'price': price, 'deviation': deviation})
# 计算绩效指标
metrics = calculate_metrics(trades)
return trades, metrics
关键点:注意看平仓逻辑里的「止盈」和「止损」是分开判断的。我曾经犯过一个错误,把止盈和止损写成了互斥条件——结果策略在偏离度刚好在阈值附近震荡时,频繁开平仓,手续费都亏掉了。
4.3 绩效指标——别只看收益率,这些指标更重要
回测跑完了,一堆数字摆在你面前。怎么看?我一般会重点关注这几个指标:
| 指标名称 | 计算公式 | 我的经验阈值 |
|---|---|---|
| 年化收益率 | (总收益率+1)^(252/交易天数)-1 | 10%-30% 比较合理 |
| 最大回撤 | max(峰值-谷值)/峰值 | 最好控制在5%以内 |
| 夏普比率 | (年化收益率-无风险利率)/年化波动率 | 大于1.5算优秀 |
| 胜率 | 盈利交易次数/总交易次数 | 50%-70% 比较正常 |
| 盈亏比 | 平均盈利/平均亏损 | 大于1.5才值得做 |
注意:夏普比率不是越高越好。我见过有人把回测参数调得特别激进,夏普比率做到3.0以上,但实盘一个月就亏了15%。为什么?因为回测数据里没有考虑滑点和流动性问题。偏离度套利策略在极端行情下,可能根本买不到你想买的价位。
4.4 回测结果分析——一个真实的案例
拿我去年做的一个测试来说吧。选了2023年1月到6月的沪深300相关可转债数据,参数设置如下:
- 开仓阈值:±2%
- 止盈阈值:0.5%
- 止损阈值:1%
- 每次交易固定仓位:10%
跑出来的结果是这样的:
| 指标 | 数值 |
|---|---|
| 总交易次数 | 87次 |
| 盈利次数 | 52次 |
| 亏损次数 | 35次 |
| 胜率 | 59.8% |
| 总收益率 | 8.7% |
| 年化收益率 | 17.4% |
| 最大回撤 | 3.2% |
| 夏普比率 | 1.82 |
说实话,这个结果比我预想的要好。最大回撤只有3.2%,说明策略在震荡市里表现稳定。但注意看,胜率不到60%——也就是说,每10次交易里有4次是亏的。这很正常,偏离度套利本身就是赚「概率钱」,不是每笔都能赚。
避坑指南:我曾经在回测时忽略了一个细节——手续费。可转债的交易手续费虽然低,但如果你频繁交易(比如每天开平仓好几次),手续费会吃掉你很大一部分利润。建议在回测代码里加上万分之二的滑点和手续费,这样结果更接近实盘。
4.5 参数敏感性分析——别让参数「骗」了你
你想想看,如果我把开仓阈值从2%改成1.5%,结果会怎样?或者把止损从1%改成0.5%?
我习惯的做法是,对每个关键参数做一次「敏感性测试」。比如:
# 参数敏感性测试示例
thresholds = [0.01, 0.015, 0.02, 0.025, 0.03]
results = []
for th in thresholds:
trades, metrics = backtest_deviation_strategy(data, entry_threshold=th)
results.append({
'threshold': th,
'return': metrics['annual_return'],
'max_drawdown': metrics['max_drawdown'],
'sharpe': metrics['sharpe_ratio']
})
# 输出结果
pd.DataFrame(results)
跑完之后你会发现,阈值从2%降到1.5%时,交易次数从87次增加到156次,但胜率从59.8%降到了51.2%。为什么?因为阈值越低,越容易触发开仓,但很多「假信号」也被纳入了。说白了,阈值太低就是在赌运气。
我的结论:偏离度套利策略的「甜蜜点」通常在2%-3%之间。低于2%信号太多,高于3%信号太少。你可以根据自己的风险偏好在这个范围内调整。
好了,回测这部分就聊到这儿。记住一句话:回测是为了发现策略的「弱点」,而不是为了证明它有多完美。如果你发现回测结果好得不像真的——嗯,那它大概率就是假的。