4、成本归因基础:交易成本分解、冲击成本、滑点成本、佣金与税费
做量化策略,很多人上来就盯着Alpha、夏普比率。但我得说一句大实话:交易成本才是吃掉利润的隐形杀手。尤其是做商品期货,杠杆高、频率快,成本稍微没算准,回测里漂亮的曲线,实盘直接变成心电图。
我个人习惯,在搭建任何策略之前,先把成本模型搭好。说白了,你赚的钱,有一半可能是给交易所和滑点打工的。今天我们就来拆解一下,交易成本到底由哪些部分构成。
4.1 交易成本分解:别小看每一笔
交易成本可以分成两大类:显性成本和隐性成本。显性成本就是你直接付出去的钱,比如佣金、印花税、过户费。隐性成本则更隐蔽——冲击成本、滑点、延迟成本,这些在回测里很容易被忽略。
我记得有一次帮朋友复盘一个螺纹钢的日内策略,回测年化收益30%,实盘跑了两个月只剩8%。一查,问题全出在成本上。回测时我用了固定滑点1跳,但实盘里遇到行情剧烈波动,滑点经常跑到3-4跳。这就是典型的隐性成本没算够。
总交易成本 = 佣金 + 税费 + 冲击成本 + 滑点成本 + 持仓成本(隔夜利息/仓储费)
4.2 冲击成本:你的订单就是市场的噪音
冲击成本,说白了就是你的订单对市场价格造成的影响。你买得越多,价格被你推得越高;你卖得越多,价格被你砸得越低。这个成本在回测里是算不出来的,因为回测假设你的订单不影响市场。
我一般用这个公式估算冲击成本:
# 冲击成本估算模型(基于订单簿深度)
def impact_cost(volume, avg_depth, spread):
"""
volume: 你的交易量(手)
avg_depth: 订单簿前5档平均深度(手)
spread: 当前买卖价差(tick)
"""
impact_ratio = volume / (avg_depth * 5) # 超过5档深度就算大单
cost = impact_ratio * spread * 0.5 # 线性近似
return min(cost, spread * 2) # 上限设为2个spread
嗯,这里要注意:冲击成本不是线性的。小单子几乎没影响,但一旦你的单子超过了订单簿第一档的深度,价格就会开始往不利方向移动。我在做铁矿石策略时,发现只要单笔超过50手,冲击成本就开始显著上升。
4.3 滑点成本:你看到的价,不是你成交的价
滑点成本跟冲击成本有点像,但本质不同。滑点是因为延迟和流动性不足导致的成交价格偏离。你下单的时候看到买一价是5000,等你的单子到了交易所,买一可能已经变成5002了。
为什么会这样?因为网络延迟、交易所撮合排队、对手方撤单。尤其是在快市行情下,滑点会急剧放大。
我曾经做过一个测试:在橡胶期货上,用市价单下单100手,统计了1000次交易。结果发现,平均滑点达到1.8个tick,最大滑点达到7个tick。这个数据让我重新审视了策略的止损逻辑——如果止损单触发时滑点太大,止损就变成了摆设。
| 品种 | 平均滑点(tick) | 最大滑点(tick) | 建议回测滑点 |
|---|---|---|---|
| 螺纹钢 | 0.8 | 3 | 1 |
| 铁矿石 | 1.2 | 5 | 1.5 |
| PTA | 0.6 | 2 | 1 |
| 沪铜 | 1.5 | 6 | 2 |
4.4 佣金与税费:看似小钱,积少成多
佣金和税费是显性成本,相对容易计算。但很多人会忽略一个细节:不同期货公司的佣金标准不一样,而且可以谈。
我个人的习惯是,在策略开发阶段就按交易所标准的1.5倍来算佣金。为什么?因为实盘时你拿到的佣金可能比交易所标准高,而且还有各种杂费(比如交割费、仓单费)。
商品期货的主要费用包括:
- 交易手续费: 按成交金额的万分之几收取,不同品种差异很大。比如螺纹钢是万分之一,沪铜是万分之零点五。
- 印花税: 商品期货目前没有印花税,但股指期货有。
- 交割费: 如果你持仓进入交割月,需要缴纳交割费。对于投机策略,建议在交割月前平仓。
- 仓储费/持仓费: 部分品种有隔夜持仓费,比如原油、燃料油。
# 佣金与税费计算示例
def calc_trade_cost(price, volume, commission_rate=0.0001):
"""
price: 成交价格
volume: 成交量(手)
commission_rate: 手续费率(默认万分之一)
"""
turnover = price * volume * 10 # 假设每手10吨
commission = turnover * commission_rate
# 商品期货无印花税
total_cost = commission
return total_cost
4.5 综合成本模型:把一切串起来
好了,现在我们把所有成本串成一个完整的模型。我个人习惯在策略回测中,直接用一个函数来计算每笔交易的总成本:
def total_cost_model(price, volume, side, market_depth, spread, latency_ms):
"""
综合交易成本模型
side: 'buy' or 'sell'
market_depth: 订单簿前5档平均深度
spread: 当前价差(tick)
latency_ms: 网络延迟(毫秒)
"""
# 1. 显性成本
commission = price * volume * 10 * 0.0001 # 万分之一手续费
tax = 0 # 商品期货无印花税
# 2. 冲击成本
impact_ratio = volume / (market_depth * 5)
impact = impact_ratio * spread * 0.5
impact = min(impact, spread * 2)
# 3. 滑点成本(基于延迟和流动性)
slippage_base = 0.5 * spread # 基础滑点
latency_factor = latency_ms / 100 # 每100ms延迟增加0.5个tick滑点
slippage = slippage_base + latency_factor * 0.5 * spread
# 4. 总成本(以价格单位表示)
total_cost_per_unit = commission / (volume * 10) + impact + slippage
return total_cost_per_unit
这个模型虽然简单,但已经能覆盖大部分场景。你想想看,如果回测时连这些成本都没算进去,那出来的结果基本就是自欺欺人。
最后说一句:成本归因不是一次性的工作。随着市场环境变化,流动性、波动率、佣金政策都会变。我每季度都会重新评估一次成本模型,看看有没有需要调整的地方。毕竟,赚到的钱,只有装进口袋的才是真的。