4. TWAP策略回测:历史数据回测、绩效指标计算、滑点模型、交易成本估算
回测,说白了就是让策略在历史数据上跑一遍。我见过太多人,策略回测时赚得盆满钵满,一上实盘就亏得底掉。为什么?因为回测没做好,尤其是TWAP这种高频执行策略,细节决定生死。
今天我们就来拆解TWAP回测的四个核心模块。嗯,每个模块我都踩过坑,咱们一个一个说。
4.1 历史数据回测:别让数据坑了你
回测的第一步,是拿到干净的历史数据。我个人习惯用1分钟K线做TWAP回测,因为TWAP本身就是分钟级切片执行。数据源我常用的是交易所的Level-2行情,或者第三方数据商的tick数据聚合。
数据清洗的几个要点:
- 缺失值处理:遇到停牌、熔断,直接跳过该时间段,别用插值法补。我在项目中遇到过用前值填充,结果回测曲线漂亮得不像话,实盘直接傻眼。
- 异常值过滤:比如某根K线的成交量突然放大100倍,大概率是数据错误。我一般用3倍标准差法剔除。
- 复权处理:股票有分红送股,期货有换月跳空。TWAP是执行策略,不是选股策略,所以复权要谨慎。我个人建议用后复权,保持价格连续性。
下面是一个简单的数据加载和清洗示例:
import pandas as pd
import numpy as np
def load_and_clean_data(file_path):
df = pd.read_csv(file_path, parse_dates=['time'])
df.set_index('time', inplace=True)
# 剔除停牌时段(成交量为0)
df = df[df['volume'] > 0]
# 剔除异常值(成交量超过3倍标准差)
mean_vol = df['volume'].mean()
std_vol = df['volume'].std()
df = df[np.abs(df['volume'] - mean_vol) <= 3 * std_vol]
return df
# 加载1分钟K线数据
data = load_and_clean_data('btc_usdt_1m.csv')
print(f"数据范围:{data.index[0]} 至 {data.index[-1]}")
print(f"有效K线数:{len(data)}")
4.2 绩效指标计算:别只看收益率
TWAP策略的绩效评估,和选股策略完全不同。我们不看Alpha、Beta,我们看的是执行效率和滑点控制。
核心指标包括:
- 实现价格 vs TWAP基准价:这是最直接的指标。实现价格越接近TWAP基准价,说明执行越好。
- 滑点比率:(实现价格 - TWAP基准价) / TWAP基准价。正数表示买贵了或卖便宜了,负数表示执行得比基准好。
- 完成率:实际成交数量 / 目标数量。TWAP是时间切片,如果某个切片没成交完,后面的切片要补上,完成率就是衡量这个的。
- 市场冲击成本:因为你的订单而导致的额外价格变动。这个比较难算,后面滑点模型会细说。
我一般会生成一个绩效报告表格:
| 指标 | 说明 | 理想值 |
|---|---|---|
| 实现价格 | 实际成交的加权平均价 | 接近TWAP基准 |
| 滑点比率 | 实现价与基准价的偏差比例 | ±0.05%以内 |
| 完成率 | 实际成交/目标数量 | 99%以上 |
| 市场冲击成本 | 订单对价格的瞬时影响 | 越低越好 |
重点提醒:TWAP策略的终极目标不是赚钱,而是以最小的滑点完成交易。所以回测时,别盯着收益率看,要看滑点比率。我曾经有个策略收益率很高,但滑点比率高达0.3%,实盘一跑,收益全被滑点吃掉了。
4.3 滑点模型:最容易被低估的部分
滑点模型,说白了就是模拟你的订单进入市场后,价格会怎么变。这是TWAP回测中最难、也最关键的一环。
常见的滑点模型有三种:
- 固定滑点模型:假设每次成交都比当前价差一个固定值,比如1个tick。简单粗暴,但不够真实。
- 比例滑点模型:按成交金额的固定比例计算滑点,比如0.01%。比固定滑点好一点,但依然粗糙。
- 基于订单簿的滑点模型:模拟真实的订单簿,你的订单会吃掉盘口的挂单,导致价格移动。这是最接近实盘的模型。
我个人强烈推荐第三种。虽然计算量大,但值得。下面是一个简化的订单簿滑点模型:
def simulate_slippage(order_book, order_qty, side='buy'):
"""
模拟订单簿滑点
order_book: 包含价格和数量的DataFrame
order_qty: 订单数量
side: 'buy' 或 'sell'
"""
total_cost = 0
remaining_qty = order_qty
if side == 'buy':
levels = order_book['asks'] # 卖盘
else:
levels = order_book['bids'] # 买盘
for price, qty in levels:
if remaining_qty <= 0:
break
trade_qty = min(qty, remaining_qty)
total_cost += trade_qty * price
remaining_qty -= trade_qty
if remaining_qty > 0:
# 订单簿深度不够,用最后价格+滑点估算
total_cost += remaining_qty * price * 1.001
avg_price = total_cost / order_qty
slippage = (avg_price - order_book['mid_price']) / order_book['mid_price']
return avg_price, slippage
4.4 交易成本估算:别让手续费吃掉利润
交易成本不只是手续费,还有印花税、过户费、平台费等等。TWAP策略交易频率高,成本累积起来非常可观。
交易成本的构成:
- 显性成本:手续费、印花税、过户费。这些是明码标价的,直接算就行。
- 隐性成本:滑点、市场冲击、延迟成本。这些需要估算,前面滑点模型已经覆盖了一部分。
我一般会在回测中加一个成本计算函数:
def calculate_trading_cost(trade_value, fee_rate=0.001, tax_rate=0.001):
"""
计算交易成本
trade_value: 交易金额
fee_rate: 手续费率(默认0.1%)
tax_rate: 印花税率(默认0.1%)
"""
fee = trade_value * fee_rate
tax = trade_value * tax_rate
total_cost = fee + tax
return total_cost
# 示例:买入100万市值的BTC
cost = calculate_trading_cost(1000000)
print(f"交易成本:{cost:.2f} 元") # 输出:2000元
你看,100万的交易,成本就是2000块。如果TWAP策略每天交易10次,一个月就是60万的成本。嗯,这个数字是不是很吓人?
知识体系总览
下面这张图,是我自己整理的回测知识框架。你看一眼,心里就有数了:
这张图把四个模块串起来了。你仔细看,每个模块之间都有依赖关系——数据质量影响绩效计算,滑点模型决定成本估算。任何一个环节出问题,回测结果都是废纸。
好了,TWAP回测的核心内容就这些。记住一句话:回测是用来发现问题的,不是用来证明策略赚钱的。把滑点算准,把成本算足,你的TWAP策略在实盘里才能站得住脚。
公众号:蓝海资料掘金营,微信deep3321