把回测成本模型校准到真实成交回报
从券商流水反推冲击成本系数,让回测和实盘的差距落在可解释范围。
固定 bp 不够用
回测里写死「单边 8bp」的问题在于它和交易量无关。同一个策略资金从 500 万涨到 5000 万,换手率不变,但实际成本可能翻三倍。一旦策略规模变化,回测就彻底失效了。
业界通用的形式是平方根律的变体:成本随委托量占日均成交额的比例呈幂函数上升。
def slippage_bps(q, adv, sigma, k=0.9, alpha=0.6): """冲击成本估计(单位:bp) q : 本次委托股数 adv : 该标的近 20 日平均成交股数 sigma : 该标的日波动率 k : 冲击系数,需要用真实成交回报校准 alpha : 幂次,股票通常落在 0.5 ~ 0.7 """ participation = q / adv return k * sigma * 1e4 * participation ** alpha
用流水反推 k
k 是唯一需要你自己校准的参数。做法是把每一笔真实成交和它的决策价(下单那一刻的中间价)对比,算出实际滑点,然后对模型做最小二乘拟合。
import numpy as np fills['real_bps'] = (fills['fill_px'] / fills['decision_px'] - 1) * 1e4 fills.loc[fills['side'] == 'sell', 'real_bps'] *= -1 x = fills['sigma'] * 1e4 * (fills['qty'] / fills['adv']) ** 0.6 y = fills['real_bps'] k_hat = float(np.linalg.lstsq(x.values[:, None], y.values, rcond=None)[0][0]) resid = y - k_hat * x print(f'k = {k_hat:.3f}, 残差标准差 = {resid.std():.1f} bp')
样本量的最低要求
至少要有几百笔覆盖不同参与率的成交,且不能全来自同一段行情。如果你的流水里参与率都挤在 0.5% 附近,拟合出来的 k 只在那个点附近有效,外推到 5% 会严重低估。
校准之后再看回测
把校准好的成本函数放回回测,通常会看到年化收益掉 3 到 8 个点,换手越高掉得越多。这个落差不是坏消息——它把「策略能装多少钱」这个问题变成了可计算的:解 k * sigma * (q/adv)^0.6 = 预期收益,得到的 q 就是容量上界。
下一篇讲怎么在这个成本函数下反过来优化换手,也就是把交易成本写进目标函数。
成本模型冲击成本执行