16、订单拆分策略:TWAP、VWAP、Iceberg 订单、隐藏订单
做量化交易的朋友,迟早会撞上一个问题:
「我的大单子一挂出去,市场就跟我对着干。」
你买,价格就涨。你卖,价格就跌。好像市场里有一双眼睛,专门盯着你的订单。
其实不是市场针对你。是你的订单太大了,直接把盘口吃穿了。我早年做回测时,策略跑出来年化80%,一上实盘就亏钱。后来才发现,回测里我假设「想买就能买到」,但实盘里我的单子一进去,滑点就把利润吃光了。
所以,订单拆分策略就来了。说白了,就是把大象塞进冰箱,分几步走。
核心思想:大单拆小单,降低市场冲击,隐藏交易意图。
16.1 TWAP:时间加权平均价格
TWAP,全称 Time-Weighted Average Price。名字挺唬人,其实逻辑很简单:把时间切成片,每片里扔一点单子进去。
比如你想在1小时内买入10万股的BTC。TWAP的做法是:
- 把1小时切成60个1分钟的切片
- 每个切片里,下单 100000 / 60 ≈ 1667 股
- 不管价格怎么变,到点就下单
我刚开始用TWAP时,觉得这玩意儿太机械了。后来发现,它最大的好处是可预测。你算好时间,算好量,执行偏差基本可控。
适用场景:市场流动性一般,你不想暴露意图,且对价格没有太强判断。比如做市商调仓、指数基金申赎。
代码实现也很直白:
def twap(total_qty, total_seconds, slice_seconds=60):
slices = total_seconds // slice_seconds
qty_per_slice = total_qty / slices
for i in range(slices):
# 每个切片执行一次下单
place_order(qty=qty_per_slice)
time.sleep(slice_seconds)
嗯,这里要注意:TWAP有个硬伤——它不看市场深度。如果某个切片里盘口很薄,你的单子照样会把价格打飞。我曾在ETH上吃过这个亏,盘口只有几百个ETH,我硬是塞了5000个进去,结果滑点直接吃掉了我当天的利润。
16.2 VWAP:成交量加权平均价格
VWAP,Volume-Weighted Average Price。比TWAP聪明一点:它跟着成交量走。
逻辑是:市场成交量大的时候,我多下点;成交量小的时候,我少下点。这样我的订单就「融入」了市场正常的交易流里,不容易被发现。
具体做法:
- 先拿到历史成交量分布(比如过去N天的分钟级成交量)
- 算出每个时间片的成交量占比
- 按这个占比分配你的订单量
举个例子:
| 时间片 | 历史成交量占比 | 分配订单量(总10万股) |
|---|---|---|
| 09:30-09:31 | 5% | 5000 |
| 09:31-09:32 | 8% | 8000 |
| 09:32-09:33 | 3% | 3000 |
我个人的习惯是,VWAP配合实时成交量做动态调整。比如某个时间片实际成交量比历史高,我就多下一点。这样更贴近真实市场。
避坑指南:VWAP依赖历史数据。如果今天市场结构变了(比如突发新闻),历史分布就失效了。我曾经在财报发布日用VWAP,结果前半小时成交量暴增,我的单子全砸在最高点……嗯,从那以后我学会了加一个「异常检测」逻辑。
16.3 Iceberg 订单:冰山订单
Iceberg,冰山订单。名字很形象:你只露出水面的一小部分,大部分藏在下面。
比如你想卖10万个ETH。如果你直接挂一个卖10万的单子,盘口上所有人都能看到。做市商和量化基金立刻就会压价。但Iceberg订单会把你的10万拆成很多个小单,比如每次只显示1000个。吃掉一个,再露一个。
为什么叫冰山?因为盘口上只看到「冰山一角」。
实现逻辑:
def iceberg(total_qty, display_qty, price):
remaining = total_qty
while remaining > 0:
current_qty = min(display_qty, remaining)
place_limit_order(price=price, qty=current_qty)
remaining -= current_qty
# 等待当前显示的单子被吃掉
wait_for_fill()
你想想看,Iceberg订单最大的好处是什么?隐藏真实意图。别人以为你只想买1000个,实际上你在悄悄吸筹。我见过一些做市商,用Iceberg订单在盘口上「钓鱼」,一边挂单一边撤单,把对手耍得团团转。
注意:Iceberg订单不是所有交易所都支持。有些交易所需要你自己在客户端实现「撤单-重挂」逻辑。而且频繁撤挂可能会被交易所判定为「滥用API」,小心被封。
16.4 隐藏订单:完全隐形
隐藏订单,也叫「暗单」。比Iceberg更进一步:盘口上完全看不到你的单子。
只有当你这个价格上的所有可见单子都被吃掉后,你的隐藏单子才会被「激活」。换句话说,你的单子是「隐形」的,但它在订单簿里真实存在。
为什么要有隐藏订单?
- 避免被狙击:你的大单不会吓跑对手
- 降低冲击成本:你的单子不会让价格提前变动
- 提高成交概率:当价格到达你的位置时,你排在所有可见单子后面,但比同价位的其他隐藏单子优先
我记得有一次,我在某个山寨币上挂了一个隐藏买单。盘口上所有人都以为买盘很弱,价格一路下跌。结果跌到我的隐藏价位时,我的单子瞬间把所有的卖单全吃了。那感觉,就像打牌时手里藏了一张王牌。
使用建议:隐藏订单适合「被动吃单」策略。如果你是想主动吃单(市价单),隐藏订单没用。它只对限价单有效。
16.5 四种策略对比
| 策略 | 核心逻辑 | 隐藏程度 | 市场冲击 | 适用场景 |
|---|---|---|---|---|
| TWAP | 按时间均匀分配 | 低 | 中 | 流动性一般,无价格判断 |
| VWAP | 按成交量分配 | 中 | 低 | 流动性好,跟随市场节奏 |
| Iceberg | 分批显示小单 | 高 | 低 | 大单被动挂单,隐藏意图 |
| 隐藏订单 | 完全不可见 | 极高 | 极低 | 被动吃单,避免被狙击 |
16.6 核心逻辑流程图
下面这张图,帮你理清四种策略的选择逻辑:
你看,选择哪种策略,核心就两个问题:你是主动吃还是被动挂?你想藏多深?
主动吃单,用TWAP或VWAP跟着市场节奏走。被动挂单,用Iceberg或隐藏订单把真实意图藏起来。如果条件允许,组合使用效果更好——比如用VWAP决定每个时间片下多少量,再用Iceberg把每个时间片里的单子拆成小单。
最后说一句:没有完美的策略。TWAP太死板,VWAP依赖历史数据,Iceberg容易被算法识别,隐藏订单成交优先级低。真正的高手,是根据市场状态动态切换。流动性好的时候用VWAP,流动性差的时候用Iceberg,遇到大行情时直接上隐藏订单。
嗯,这些经验,都是我用真金白银换来的。