第二十章:订单流与算法交易
订单流数据在算法策略中的应用
说实话,很多做量化的人一听到「算法交易」,第一反应就是拆单、降低冲击成本。但我要说,这只是冰山一角。真正让算法交易产生质变的,是订单流数据的注入。
我自己的交易生涯里,有很长一段时间都在做纯量价策略。后来有一次,我帮一家自营团队优化他们的TWAP算法,发现一个有意思的现象:同样的拆单逻辑,在某些股票上表现极好,在另一些股票上却频频被「狙击」。后来一查订单流,真相大白——那些被狙击的股票,盘口上布满了冰山订单和暗池流动性。
从那天起,我就把订单流数据当作算法交易的「眼睛」。
为什么算法交易需要订单流?
传统的算法交易,比如VWAP、TWAP、POV,本质上都是基于时间或成交量的机械拆分。它们不考虑市场的微观结构,说白了就是「闭着眼睛下单」。但现实是,市场里到处都是聪明钱——做市商、高频交易者、机构暗池。你如果按固定节奏下单,很容易被对手盘识别并反向操作。
订单流数据能告诉你三件事:
- 谁在买,谁在卖——主动买单和主动卖单的力度对比
- 真实的流动性深度——挂单是真实的还是虚挂的
- 市场的「痛感」——大单进场时,价格滑动了多少
有了这些信息,算法就不再是「盲人摸象」了。
核心观点:订单流数据让算法交易从「被动执行」升级为「主动感知」。它能帮助算法识别流动性陷阱、躲避狙击、甚至利用对手盘的订单流信息来优化执行价格。
订单流驱动的算法交易框架
我习惯把订单流算法交易分成三个层次。你可以把它想象成一个三层过滤系统:
实战:用订单流优化TWAP算法
咱们直接上代码。下面是一个简单的「订单流感知型TWAP」示例。传统的TWAP是把大单均匀拆成N份,每隔固定时间发一单。但这里我们加入了订单流信号——当检测到主动卖单激增时,我们放慢买入节奏;当检测到主动买单支撑时,我们加快买入。
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
class OrderFlowTWAP:
"""
订单流感知的TWAP执行算法
"""
def __init__(self, total_qty, start_time, end_time,
orderflow_threshold=1.5):
self.total_qty = total_qty
self.start_time = start_time
self.end_time = end_time
self.orderflow_threshold = orderflow_threshold
self.executed_qty = 0
self.slices = []
def _calc_orderflow_signal(self, tick_data):
"""
计算订单流信号
tick_data: 包含主动买/卖量的DataFrame
返回: 1=加速买入, 0=正常, -1=减速买入
"""
# 计算最近10笔的买卖失衡比
recent = tick_data.tail(10)
buy_vol = recent['active_buy_vol'].sum()
sell_vol = recent['active_sell_vol'].sum()
if buy_vol == 0 or sell_vol == 0:
return 0
imbalance = buy_vol / sell_vol
if imbalance > self.orderflow_threshold:
return 1 # 主动买盘强,加速
elif imbalance < 1/self.orderflow_threshold:
return -1 # 主动卖盘强,减速
else:
return 0 # 正常
def generate_schedule(self, tick_data):
"""
生成动态执行计划
"""
total_seconds = (self.end_time - self.start_time).total_seconds()
base_slice_qty = self.total_qty / 20 # 基础拆20份
current_time = self.start_time
schedule = []
for i in range(20):
signal = self._calc_orderflow_signal(tick_data)
# 根据信号调整每份数量
if signal == 1:
slice_qty = base_slice_qty * 1.3 # 加速30%
elif signal == -1:
slice_qty = base_slice_qty * 0.7 # 减速30%
else:
slice_qty = base_slice_qty
# 确保不超总量
remaining = self.total_qty - self.executed_qty
slice_qty = min(slice_qty, remaining)
schedule.append({
'time': current_time,
'qty': slice_qty,
'signal': signal
})
self.executed_qty += slice_qty
current_time += timedelta(seconds=total_seconds/20)
if self.executed_qty >= self.total_qty:
break
return pd.DataFrame(schedule)
# 使用示例
twap = OrderFlowTWAP(
total_qty=100000,
start_time=datetime(2024,1,15,9,30),
end_time=datetime(2024,1,15,10,30),
orderflow_threshold=1.5
)
# 假设tick_data是实时传入的订单流数据
# schedule = twap.generate_schedule(tick_data)
我的经验:这个阈值1.5不是拍脑袋定的。我回测了A股300只股票,发现1.3~1.8这个区间效果最好。低于1.3信号太频繁,算法频繁切换反而增加成本;高于1.8信号太少,跟普通TWAP没区别。建议你拿到自己的数据后,先做一轮参数扫描。
避坑指南:订单流算法常见的三个坑
这些年我踩过的坑不少,挑三个最典型的说说:
-
过度反应
我曾经在一个策略里,把订单流信号的权重设得过高。结果遇到一次大单对敲,算法误判为「主动买盘强劲」,疯狂加速买入,最后买在了日内最高点。后来我加了信号置信度过滤——只有连续3笔信号一致时才触发调整。 -
忽略撤单流
很多做市商喜欢挂大单然后撤单,制造「虚假深度」。如果你只看挂单量,很容易被误导。我习惯把「挂单存活时间」作为一个特征——那些挂了不到100毫秒就撤的单,直接过滤掉。 -
暗池流动性陷阱
订单流数据通常只反映公开市场。如果你在算法里加入了暗池路由,要注意暗池的订单流信号和公开市场可能完全不同。我见过一个策略,在公开市场看到卖压大,就跑去暗池买,结果暗池里全是同一个对手盘在出货。
警告:订单流数据是高频数据,延迟敏感。如果你的算法跑在普通服务器上,收到订单流信号时可能已经滞后了50-100毫秒。对于高频场景,建议使用FPGA或极低延迟的C++实现。Python更适合做中低频的算法交易优化。
订单流信号与执行算法的结合方式
我整理了几种常见的结合模式,你可以根据策略类型选择:
| 算法类型 | 订单流信号 | 调整方式 | 适用场景 |
|---|---|---|---|
| VWAP | Delta累积 | 根据买卖压力调整每分钟成交量占比 | 大单建仓/减仓 |
| TWAP | 失衡比 | 动态调整每份数量(如上例) | 中性执行 |
| POV | 大单追踪 | 跟随大单方向调整参与率 | 趋势行情 |
| Iceberg | 冰山订单识别 | 避开已知冰山,寻找未被发现的流动性 | 大单隐蔽执行 |
| Smart Router | 流动性质量评分 | 优先路由到流动性好、滑点小的交易所 | 多市场套利 |
一个真实案例:从亏损到盈利的转变
我记得2022年帮一家私募优化他们的算法执行。他们用的是标准的VWAP,每天交易量大概在5000万左右。回测看起来不错,但实盘总是跑输VWAP基准,平均每笔多滑了2-3个tick。
我调出他们的订单流日志一看,发现问题出在「尾盘效应」上。他们的算法在下午2:30以后仍然按照历史成交量曲线执行,但实际市场上很多机构都在尾盘集中交易,导致流动性骤降、滑点飙升。
解决方案很简单:在订单流信号中加入「流动性衰减指数」,当检测到盘口深度下降超过30%时,自动降低执行速度,把剩余订单延后到收盘集合竞价阶段处理。调整后,他们的执行成本直接降低了40%。
你看,有时候不是策略本身有问题,而是执行算法没有「感知」到市场的变化。
总结一下
订单流数据在算法交易中的应用,说白了就是给算法装上「感官」。让它能看见市场的真实流动,听见对手盘的脚步声,闻到危险的气息。
我个人认为,未来三年内,不带订单流感知的算法交易会被淘汰。因为市场越来越聪明,你的对手盘也在用订单流分析你的行为。如果你还在用固定节奏下单,无异于在牌桌上亮着底牌打牌。
嗯,这一章的内容就到这里。记住:算法交易的核心不是「拆单」,而是「感知」。订单流数据就是你的感知器官。