第十八章 回测框架搭建:如何搭建回测框架,验证套利策略的有效性
说实话,波动率曲面套利这个领域,最让人头疼的不是策略本身,而是怎么验证它到底能不能赚钱。
我见过太多人,策略逻辑写得天花乱坠,一上实盘就亏得亲妈都不认识。为什么?因为回测框架没搭好。你想想看,一个连历史数据都跑不通的策略,凭什么相信它能赚钱?
这一章,我就把我这些年踩过的坑、积累的经验,全盘托出。咱们一步步来,把回测框架搭得结结实实。
18.1 回测框架的核心要素
一个靠谱的回测框架,说白了就三个东西:数据、逻辑、评估。缺一不可。
| 要素 | 说明 | 我的经验 |
|---|---|---|
| 数据 | 期权链数据、标的行情、无风险利率 | 数据质量决定回测下限,我吃过亏 |
| 逻辑 | 套利信号识别、交易执行、仓位管理 | 逻辑要可复现,别搞玄学 |
| 评估 | 收益、风险、夏普、最大回撤 | 别只看收益,回撤才是亲爹 |
我个人习惯,先把这三个模块拆开写,最后再拼起来。这样调试起来方便,出了问题也知道去哪找。
18.2 数据层:打好地基
数据是回测的命根子。没有干净的数据,一切都是扯淡。
需要哪些数据?
- 期权合约的每日行情:开盘价、收盘价、最高价、最低价、成交量、持仓量
- 标的资产价格序列:至少5分钟级别,最好tick级
- 无风险利率曲线:用国债收益率或者SHIBOR都行
- 隐含波动率曲面:这个需要自己算,或者从数据商买
数据清洗要点:
- 剔除异常值:比如价格跳空、成交量异常放大
- 处理缺失值:用前向填充或者插值法
- 对齐时间戳:不同数据源的时间戳可能不一致,要统一
18.3 逻辑层:策略的核心
这一层就是你的套利策略本身。我一般把它拆成三个步骤:
- 信号生成:什么时候发现套利机会?
- 交易执行:怎么进场、怎么出场?
- 仓位管理:每次下多少手?
举个例子,一个简单的波动率曲面套利策略:
# 伪代码示例
def generate_signals(vol_surface):
# 1. 计算各期限的隐含波动率
iv_curve = calc_iv_curve(vol_surface)
# 2. 拟合波动率曲面
fitted_surface = fit_svi_model(iv_curve)
# 3. 寻找偏离点
residuals = iv_curve - fitted_surface
# 4. 生成信号
signals = []
for i, res in enumerate(residuals):
if abs(res) > threshold:
signals.append({
'strike': strikes[i],
'expiry': expiries[i],
'direction': 'long' if res < 0 else 'short',
'size': abs(res) / threshold
})
return signals
18.4 评估层:别被数字骗了
回测结果出来了,一堆数字摆在你面前。怎么看?
核心指标:
| 指标 | 计算公式 | 我的看法 |
|---|---|---|
| 年化收益率 | (最终净值/初始净值)^(252/交易日数) - 1 | 别只看这个,要结合回撤看 |
| 最大回撤 | 从峰值到谷底的最大跌幅 | 这个比收益率重要十倍 |
| 夏普比率 | (年化收益 - 无风险利率) / 年化波动率 | 大于1算及格,大于2算优秀 |
| 胜率 | 盈利交易次数 / 总交易次数 | 套利策略胜率一般很高,但别太信 |
你想想看,一个策略年化50%,但最大回撤30%,你敢上实盘吗?反正我不敢。我一般要求最大回撤不超过10%,夏普比率不低于1.5。
18.5 回测框架的完整流程
好了,三个模块都讲完了。怎么把它们串起来?
我画了一张流程图,你看一眼就明白了:
你看,整个流程其实不复杂。数据进来,跑逻辑,出结果,评估,不满意就回去改。循环往复,直到你满意为止。
18.6 回测中的常见陷阱
做回测这么多年,我总结了几条血泪教训:
- 幸存者偏差:只用存续到现在的合约做回测,那些退市的、被行权的全被忽略了。结果回测收益虚高,实盘直接崩。
- 未来函数:用未来的数据去判断过去的信号。比如用收盘价算指标,却在盘中就执行了交易。这属于作弊。
- 交易成本忽略:期权交易的手续费、滑点、买卖价差,加起来可能吃掉你一半的利润。我一般按每手2元手续费+1个tick滑点来算。
- 过拟合:参数调得太精细,回测曲线完美,实盘一塌糊涂。记住,简单的策略往往更有效。
核心原则: 回测是验证策略的工具,不是美化策略的工具。如果回测结果好得不像真的,那它大概率就是假的。
18.7 实战:一个完整的回测示例
好了,理论讲完了。咱们来点实际的。
假设我们要回测一个「波动率曲面套利」策略,具体逻辑是:当某个期限的隐含波动率偏离拟合曲面超过2个标准差时,做多或做空该期限的跨式组合。
import pandas as pd
import numpy as np
from datetime import datetime
class VolArbitrageBacktest:
def __init__(self, data, threshold=2.0):
self.data = data
self.threshold = threshold
self.results = []
def run(self):
for date, row in self.data.iterrows():
# 1. 获取当日波动率曲面
vol_surface = row['vol_surface']
# 2. 拟合曲面
fitted = self.fit_surface(vol_surface)
# 3. 计算残差
residuals = vol_surface - fitted
# 4. 生成信号
signals = self.generate_signals(residuals)
# 5. 执行交易
pnl = self.execute_trades(signals, row)
# 6. 记录结果
self.results.append({
'date': date,
'pnl': pnl,
'signals': len(signals)
})
return pd.DataFrame(self.results)
def fit_surface(self, vol_surface):
# 这里用SVI模型拟合
# 具体实现略
pass
def generate_signals(self, residuals):
signals = []
for i, res in enumerate(residuals):
if abs(res) > self.threshold:
signals.append({
'index': i,
'direction': 1 if res < 0 else -1,
'size': abs(res)
})
return signals
def execute_trades(self, signals, market_data):
# 模拟交易执行,考虑滑点和手续费
total_pnl = 0
for signal in signals:
# 计算理论盈亏
pnl = signal['size'] * market_data['vega'] * signal['direction']
# 扣除交易成本
pnl -= 2 * signal['size'] # 每手2元手续费
total_pnl += pnl
return total_pnl
这段代码虽然简单,但框架是完整的。你只需要把数据喂进去,跑一遍,就能看到结果。
我的建议: 第一次跑回测,别急着优化参数。先看看策略在哪些时间段赚钱,哪些时间段亏钱。我经常发现,策略在波动率飙升的时候表现最好,在横盘震荡的时候表现最差。这很正常,因为套利策略本质上是在赚波动率回归的钱。
18.8 回测结果的解读
回测跑完了,一堆数字摆在你面前。怎么解读?
我一般按这个顺序看:
- 先看最大回撤:如果超过15%,直接pass,别浪费时间
- 再看夏普比率:低于1的,说明风险调整后收益不行
- 然后看胜率:套利策略胜率应该在60%以上,低于这个数说明信号质量有问题
- 最后看收益曲线:是不是平稳向上?有没有突然的暴跌?
你想想看,如果一个策略的收益曲线像过山车一样,你敢把钱放进去吗?反正我不敢。
好了,回测框架就讲到这里。记住,回测不是目的,验证才是。搭好框架,反复迭代,直到你对策略有足够的信心。到那时候,再考虑上实盘也不迟。
一句话总结: 回测框架是你的策略的「照妖镜」,别怕看到丑陋的一面。早发现,早修复,总比实盘亏钱强。