3. 市场微观结构:订单簿深度、买卖价差、市场冲击成本,如何影响套利做市策略
做套利策略,很多人一上来就盯着价差公式和回测曲线。但我得说,真正决定策略能不能活下来的,是市场微观结构。说白了,就是订单簿里那些看不见的细节。
我刚开始做跨期套利时,犯过一个低级错误。回测里年化收益漂亮得很,实盘一跑,连续亏损三天。后来才发现,问题出在订单簿深度上——我的策略假设每次都能以中间价成交,但实际市场根本不给我这个机会。
今天我们就来拆解这三个核心要素:订单簿深度、买卖价差、市场冲击成本。它们是怎么影响你的套利做市策略的?
3.1 订单簿深度:你的流动性护城河
订单簿深度,简单说就是某个价位上挂着多少单子。深度越厚,你越容易大额成交而不影响价格。
举个例子。你看到近月合约和远月合约的价差偏离了理论值,想套利。你下单买入近月,卖出远月。但如果近月合约的买一档只有10手,你一下单50手,那你的订单就会吃掉买一到买五的所有挂单,价格被你推高了。
这就是深度不足的后果。你的套利空间,被自己的交易行为吃掉了。
我个人习惯在策略上线前,先拉取目标合约的订单簿快照数据,统计每个价位的平均挂单量。如果某个合约的买一档平均只有20手,那我单笔交易量绝不超过10手。留点余地,别把自己逼到墙角。
3.2 买卖价差:你的隐性成本
买卖价差,就是买一价和卖一价之间的差距。这个差距,是你每笔交易必须付出的成本。
你想想看,你买入时按卖一价成交,卖出时按买一价成交。一来一回,价差就没了。如果价差是1个tick,你的套利空间至少得大于1个tick才能赚钱。
我记得有一次做螺纹钢的跨期套利,价差波动只有2个tick,但买卖价差就占了1个tick。这意味着我的策略只有50%的理论空间能转化为实际利润。后来我调整了策略,只在价差超过3个tick时才开仓,才勉强跑出正收益。
| 合约类型 | 平均买卖价差(tick) | 建议最小套利空间 |
|---|---|---|
| 股指期货(IF) | 0.2 - 0.5 | ≥ 1 tick |
| 商品期货(螺纹钢) | 1 - 2 | ≥ 3 tick |
| 加密货币(BTC永续) | 0.5 - 1 | ≥ 2 tick |
3.3 市场冲击成本:你越做,价差越大
市场冲击成本,就是你的订单对市场价格造成的影响。你下单越大,冲击成本越高。
做套利策略时,这个问题尤其致命。因为你通常需要同时下两笔订单(一买一卖),如果其中一笔订单造成了较大的市场冲击,你的套利组合就会失衡。
我曾经做过一个测试:在深度较浅的合约上,用市价单执行10手的套利交易。结果发现,第一笔订单成交后,第二笔订单的价格已经滑了2个tick。原本2个tick的套利空间,瞬间变成0。
为什么会这样?因为你的第一笔订单改变了订单簿的平衡,市场做市商看到大单进来,会迅速调整报价。等你第二笔订单进来时,价格已经变了。
3.4 三者如何联动影响策略
这三个因素不是孤立的。它们会互相影响,形成一个闭环。
- 深度浅 → 冲击成本高:挂单少,你的订单容易推动价格
- 价差大 → 套利空间被压缩:进出成本高,需要更大的价差才能盈利
- 冲击成本高 → 实际成交价偏离预期:回测中的理论利润,实盘中可能消失
我一般会在策略中设置一个综合过滤器:只有当预期套利空间 > 买卖价差 + 预估冲击成本 + 安全垫(至少1个tick)时,才执行交易。
3.5 如何量化这些因素
量化交易,说到底就是要把模糊的概念变成可计算的指标。下面是我常用的几个指标:
# 计算订单簿深度
def calc_depth(order_book, levels=5):
"""计算前N档的总挂单量"""
bid_depth = sum([level['size'] for level in order_book['bids'][:levels]])
ask_depth = sum([level['size'] for level in order_book['asks'][:levels]])
return bid_depth, ask_depth
# 计算买卖价差
def calc_spread(order_book):
"""以tick为单位计算价差"""
best_bid = order_book['bids'][0]['price']
best_ask = order_book['asks'][0]['price']
tick_size = 0.01 # 根据合约调整
spread_ticks = (best_ask - best_bid) / tick_size
return spread_ticks
# 估算冲击成本
def estimate_impact(order_book, order_qty):
"""估算买入order_qty手所需的平均滑点"""
total_cost = 0
remaining = order_qty
for level in order_book['asks']:
if remaining <= 0:
break
trade_qty = min(remaining, level['size'])
total_cost += trade_qty * level['price']
remaining -= trade_qty
avg_price = total_cost / order_qty
mid_price = (order_book['bids'][0]['price'] + order_book['asks'][0]['price']) / 2
impact = (avg_price - mid_price) / mid_price
return impact
这段代码虽然简单,但很实用。我每次上线新策略前,都会用这个函数跑一遍历史数据,看看冲击成本到底有多大。
3.6 知识体系图
下面这张图,把这三个因素的关系梳理清楚了。你可以把它当作策略设计的检查清单。
嗯,这张图把整个逻辑串起来了。你设计策略时,可以对照着检查:我的策略有没有考虑深度?有没有预留价差空间?冲击成本算进去了吗?
最后说一句。市场微观结构这东西,光看书没用。我建议你拿真实数据跑一跑,看看你的目标合约在实盘中到底是什么样子。数据不会骗人。