16. 算法交易与深度:TWAP/VWAP对深度的影响、冰山订单、暗池

聊到算法交易对市场深度的影响,我得先坦白一件事——我入行头两年,一直觉得TWAP和VWAP就是个下单工具,跟市场深度没啥关系。直到有一次,我写了个TWAP策略跑在流动性极差的合约上,结果直接把买一价推高了三个tick。嗯,从那以后我才真正开始琢磨:算法交易到底是怎么“吃掉”深度的?

16.1 TWAP与VWAP:时间切片 vs 成交量切片

先说说这两个最基础的算法。TWAP(Time-Weighted Average Price)和VWAP(Volume-Weighted Average Price),说白了都是把大单拆成小单。但拆法不一样,对市场深度的影响也完全不同。

算法 切片逻辑 对深度的影响
TWAP 按时间均匀切片 在低流动性时段容易造成深度塌陷
VWAP 按历史成交量分布切片 相对平滑,但遇到异常放量时也会冲击

我个人习惯用VWAP多一些。为什么?因为TWAP在午盘休市前那段时间,经常把单子砸在深度最薄的时候。你想想看,市场本来就没多少人交易,你一个算法还在那机械地每秒发单,这不就是往平静的湖面扔石头吗?

核心观点:TWAP适合流动性充足的市场,VWAP更适合流动性分布不均的市场。选错了,你的订单就是在“硬吃”深度。

16.2 冰山订单:藏在水面下的深度

冰山订单(Iceberg Order)是我个人非常喜欢的一个工具。它的逻辑很简单:只暴露订单总量的一小部分,剩下的藏在“水下”。

举个例子,你想买10万手,但只显示2000手在盘口上。每次这2000手被吃掉,系统自动再挂出2000手。直到10万手全部成交。

我在项目中遇到过这样一个场景:某个做市商在ETH永续合约上挂了5000手的买单,但只显示了500手。其他交易者看到买一只有500手,觉得压力不大,就继续往下砸。结果每次砸穿500手,又冒出500手。连续吃了十几次才发现不对劲——这根本就是个无底洞。

避坑指南:我曾经犯过一个错误——把冰山订单的显示量设得太小。比如总订单100万,显示量只有100手。结果因为每次刷新太频繁,被交易所的反作弊系统判定为异常交易,直接限制了API权限。建议显示量至少占总量的1%-5%。

冰山订单对市场深度的影响是双面的:

  • 正面:隐藏真实意图,避免价格被推离有利位置
  • 负面:反复刷新订单会增加系统负载,也可能被高频交易者“钓”出来

16.3 暗池:看不见的深度

暗池(Dark Pool)这东西,说白了就是一个不公开订单簿的交易场所。你在暗池里挂单,别人看不到你的价格和数量。只有匹配成功时,成交信息才会被公布。

我记得2018年的时候,有个客户问我:“暗池是不是就是黑市?” 我笑了,其实不是。暗池是合法的,主要用来处理大额订单,避免对公开市场造成冲击。

暗池对市场深度的影响很有意思:

  • 它把一部分流动性从公开市场“吸走”了
  • 公开市场的深度变薄,价格更容易被小单推动
  • 但暗池内部的深度很厚,大单可以在里面悄悄成交

注意:暗池不是万能的。我见过有人把暗池当成“免冲击”神器,结果在暗池里挂了1个亿的卖单,等了三天都没成交。为什么?因为暗池里没有对手盘。暗池的深度是“被动深度”,需要有人主动来吃你的单子。

16.4 算法交易如何“吃掉”深度

咱们来画一张图,看看算法交易到底是怎么影响市场深度的。

算法交易对市场深度的影响路径 算法交易 TWAP 时间切片 VWAP 成交量切片 冰山/暗池 低流动性时段 深度塌陷 按量分布 冲击相对平滑 隐藏真实意图 保护深度 市场深度变化

从这张图你能看出来,不同的算法对深度的影响路径是不一样的。TWAP和VWAP是“主动吃深度”,冰山和暗池是“被动保护深度”。

16.5 实战中的选择策略

说了这么多理论,咱们来点实际的。我一般会根据以下条件来选择算法:

  1. 订单规模 < 平均深度10%:直接用市价单,别折腾算法了
  2. 订单规模在10%-50%之间:用VWAP,配合冰山订单隐藏部分意图
  3. 订单规模 > 50%:必须用暗池+冰山组合,否则你会把盘口打穿

一个真实案例:去年我在做BTC期现套利时,需要同时开多和开空各2000张合约。如果直接挂单,盘口深度瞬间被吃掉30%。我用了VWAP+冰山组合,把订单拆成每单50张,显示量设成10张。结果整个建仓过程持续了45分钟,价格只滑动了0.2个tick。嗯,这就是算法交易对深度保护的威力。

16.6 代码示例:一个简单的冰山订单实现

最后,给你看一个我常用的冰山订单伪代码。别直接复制去生产环境用,但思路可以参考:

class IcebergOrder:
    def __init__(self, total_qty, display_qty, price, side):
        self.total_qty = total_qty      # 总数量
        self.display_qty = display_qty  # 显示数量
        self.remaining = total_qty      # 剩余数量
        self.price = price
        self.side = side                # 'buy' 或 'sell'
    
    def place_initial_order(self):
        # 首次挂单,只显示 display_qty
        qty = min(self.display_qty, self.remaining)
        self._send_order(qty, self.price)
        self.remaining -= qty
    
    def on_order_filled(self, fill_qty):
        # 订单被部分成交后,补单
        if self.remaining > 0:
            qty = min(self.display_qty, self.remaining)
            self._send_order(qty, self.price)
            self.remaining -= qty
    
    def _send_order(self, qty, price):
        # 实际发送订单到交易所
        print(f"发送 {self.side} 订单: {qty} @ {price}")

这个实现有个坑——如果市场快速波动,你的冰山订单可能会被反复吃掉,导致补单频率过高。我建议加一个最小补单间隔,比如500毫秒内不重复补单。

小技巧:冰山订单的显示量不要设成固定值。我习惯用动态显示量——当市场波动率大时,显示量设小一点;波动率小时,显示量可以适当放大。这样既能隐藏意图,又不会因为频繁补单而被系统限制。

好了,关于算法交易与市场深度的关系,今天就聊到这儿。记住一句话:算法交易不是万能的,但不懂算法交易,你在深度面前就是裸奔。