好的,我们来深度总结这份海通证券关于限价订单簿(LOB)的报告,并基于报告中的策略思路提供一个Python复现示例。
深度总结:海通证券《选股因子系列研究(七十五)——限价订单簿(LOB)的还原和应用》
报告来源: 海通证券 金融工程研究
报告标题: 选股因子系列研究(七十五)——限价订单簿(LOB)的还原和应用
发布日期: 2021年12月27日
核心作者: 冯佳睿, 余浩淼
目录:
- 引言与背景
- 限价订单簿 (LOB) 的还原
- 2.1 Level 2 行情数据简介
- 2.2 LOB 还原方法与优势
- LOB 应用 1: 模拟撮合与标准 TWAP 策略
- 3.1 模拟撮合系统的重要性
- 3.2 标准市价单与限价单 TWAP 策略对比及问题
- LOB 应用 2: 改进 TWAP 策略
- 4.1 核心问题:限价单成交概率低与强制成交成本
- 4.2 预测限价单成交概率:LOB 衍生指标
- 4.3 改进 TWAP 策略逻辑
- 4.4 模拟结果与优势
- LOB 应用 3: 挖掘高频价量因子
- 5.1 LOB 分解现有因子(以买入意愿为例)
- 5.2 LOB 指标到特征提取的思考(线性组合的瓶颈)
- 总结与展望
- 风险提示
1. 引言与背景
随着2021年5月上交所推出逐笔委托Level 2行情,沪深两市均已拥有完整的Level 2高频行情数据。本报告基于最细粒度的逐笔委托和逐笔成交数据,探索如何还原完整的限价订单簿(LOB),并将其应用于交易执行优化(特别是TWAP策略)和高频因子挖掘。
2. 限价订单簿 (LOB) 的还原
- 2.1 Level 2 行情数据简介: 报告首先介绍了沪深交易所Level 2行情的两种主要形式:快照行情(Snapshot,通常3秒更新一次或更快)和逐笔行情(Tick-by-tick,包含委托和成交细节)。指出两者在时间粒度、信息内容(如沪市逐笔委托包含撤单信息、深市逐笔成交包含撤单标识)上的差异。
- 2.2 LOB 还原方法与优势: 报告核心观点之一是,利用逐笔委托和逐笔成交数据,理论上可以**精确地还原(reconstruct)**任意时刻的LOB状态。
- 方法: 通过处理带有统一序号的逐笔委托(新增、撤单)和逐笔成交记录,按时间顺序更新订单簿中各个价位的委托量和委托队列。
- 优势:
- 高频率: 更新频率可达0.01秒级别,远超快照行情。
- 低延迟: 最大程度避免快照行情可能存在的更新延迟。
- 信息丰富: 还原后的LOB包含所有价位(理论上可达涨跌停价)的完整委托信息,以及委托单的细节(如时间、精确队列位置),远超快照的十档和前50笔。
3. LOB 应用 1: 模拟撮合与标准 TWAP 策略
- 3.1 模拟撮合系统的重要性: 高频策略回测对交易成本极其敏感。传统的假设(如按中间价或对手价成交)会严重低估成本。利用还原的LOB,可以构建模拟撮合系统(Simulated Matching Engine),更精确地模拟订单在队列中的位置、等待时间及成交过程,从而更准确地评估策略的实际表现和交易成本。
- 3.2 标准市价单与限价单 TWAP 策略对比及问题:
- 市价单TWAP: 在每个时间窗口(如3秒)平均发送市价单。优点是保证成交,缺点是可能承受较大的冲击成本。
- 限价单TWAP: 在每个时间窗口(如3秒)以本方最优价(买单挂买一,卖单挂卖一)发送限价单。未成交部分可能在窗口末(如每分钟末)以市价单强制成交。
- 问题: 模拟结果(表2)显示,对于大额订单,限价单TWAP策略表现不佳。主要原因是限价单成交概率低,导致大量订单堆积到窗口末被动以市价成交,产生巨大的强制成交成本(亏损),反而不如简单的市价单TWAP。
4. LOB 应用 2: 改进 TWAP 策略
- 4.1 核心问题:限价单成交概率低与强制成交成本: 针对标准限价单TWAP的问题,改进的核心思路是在每个下单时间点(3秒),预测接下来这个时间窗口内,是下限价单更容易成交,还是应该主动下市价单以避免后续的强制成交。
- 4.2 预测限价单成交概率:LOB 衍生指标: 如何预测?报告提出利用LOB信息构建领先指标。
- 基础LOB指标 (表3): 分为静态订单簿(各档位价格、数量、笔数)和动态订单流(指定时间窗口内新增委托、成交、撤单的统计)。
- 衍生LOB指标 (报告P12): 基于过去3秒的LOB信息,计算4个关键指标来刻画当前市场微观状态:
SheetDiff
: 静态订单簿失衡(买一量 vs 卖一量)。MatchDiff
: 近期成交失衡(买一成交 vs 卖一成交)。OrderDiff
: 近期新增订单失衡(买一新增 vs 卖一新增)。CancelDiff
: 近期撤单失衡(买一撤单 vs 卖一撤单)。
- 预测模型: 使用这4个衍生指标作为特征,构建线性回归模型,预测未来3秒的买卖盘相对成交强度(用一个滞后计算的
TradeRatio
指标作为代理目标)。模型虽然预测精度有限(R方不高,见表5),但具备一定的方向性预测能力(准确率和F-score尚可)。
- 4.3 改进 TWAP 策略逻辑:
- 在每个3秒下单点:
- 获取当前LOB状态和过去3秒的订单流信息。
- 计算4个LOB衍生指标。
- 输入回归模型,得到未来3秒成交概率的预测值 (本质是预测
TradeRatio
的符号)。 - 决策:
- 买入指令: 若预测值 > 0 (预示买方限价单相对易成交),则下限价单 (挂买一价)。否则,下市价单。
- 卖出指令: 若预测值 < 0 (预示卖方限价单相对易成交),则下限价单 (挂卖一价)。否则,下市价单。
- 在每个3秒下单点:
- 4.4 模拟结果与优势: 模拟结果(表6,图13-20)显示,对于报告测试的标的(如京东方A、证券ETF):
- 改进后的TWAP策略显著提高了限价单的成交概率。
- 大幅降低了强制成交的比例。
- 相比简单的市价单或限价单TWAP,节省了交易成本 (日均约0.3-1.4 bp)。
5. LOB 应用 3: 挖掘高频价量因子
- 5.1 LOB 分解现有因子(以买入意愿为例): 报告展示了如何用精细的LOB数据解构一个已有的高频因子(开盘后买入意愿)。原因子依赖快照和逐笔成交计算,而LOB可以将意愿分解为更细致的组成部分:净挂单金额变化、净撤单金额变化、净主动成交金额变化、净被动成交金额变化。
- 5.2 LOB 指标到特征提取的思考(线性组合的瓶颈): 报告发现,将LOB分解出的细分因子进行简单的线性组合(如IC加权),相对于原始因子,在选股效果上提升非常有限(表10)。这表明,LOB提供了极其丰富的微观结构信息,但简单的线性模型难以有效利用这些高维、非线性的信息,无法突破瓶颈。
6. 总结与展望
报告系统地介绍了利用逐笔数据还原LOB的方法及其在模拟撮合、TWAP策略优化、高频因子挖掘方面的应用。核心贡献在于提出了一种基于LOB衍生指标预测成交概率来动态选择订单类型的改进TWAP策略,并验证了其有效性。同时,报告也指出了线性模型在处理复杂LOB信息时的局限性,暗示深度学习等非线性模型可能是未来在高频数据领域进行因子挖掘和策略优化的重要方向。
7. 风险提示
报告最后提示了市场系统性风险、模型误设风险、有效因子变动风险等常规风险。
Python 策略复现 (基于报告核心思想的简化示例)
以下Python代码旨在模拟报告中改进TWAP策略的核心逻辑。请注意,这是一个高度简化的示例,主要用于演示思路,并非生产级代码。它不包含真实的数据接口、LOB还原逻辑、复杂的预测模型训练与部署。
import time
import random
import pandas as pd
import numpy as np
# --- 模拟市场环境和LOB ---
# 在实际应用中,这些函数需要对接真实的数据源和LOB重构逻辑
def get_simulated_lob_snapshot():
"""模拟获取当前LOB快照 (买一卖一)"""
bid1_price = round(10.00 + random.uniform(-0.05, 0.05), 2)
ask1_price = round(bid1_price + random.choice([0.01, 0.02]), 2)
bid1_volume = random.randint(10000, 50000)
ask1_volume = random.randint(10000, 50000)
return {
'bid1_price': bid1_price,
'bid1_volume': bid1_volume,
'ask1_price': ask1_price,
'ask1_volume': ask1_volume,
'timestamp': time.time()
}
def get_simulated_recent_activity(duration=3):
"""
模拟获取过去一段时间(如3秒)的订单流活动
返回简化的成交、挂单、撤单量 (买卖方向合并统计以便计算差异)
"""
# 简化模拟: 基于随机性模拟失衡,实际需要处理真实tick数据
match_vol_bid = random.randint(0, 10000)
match_vol_ask = random.randint(0, 10000)
order_vol_bid = random.randint(5000, 20000)
order_vol_ask = random.randint(5000, 20000)
cancel_vol_bid = random.randint(0, 5000)
cancel_vol_ask = random.randint(0, 5000)
# 模拟成交、挂单、撤单活动发生在买一/卖一价位附近
# 注意:这里的命名是为了对应报告中的计算,实际实现要复杂得多
return {
'MatchVol_B1': match_vol_bid, # 假设发生在买一价的主动卖成交量
'MatchVol_A1': match_vol_ask, # 假设发生在卖一价的主动买成交量
'OrderVol_B1': order_vol_bid, # 假设挂在买一价的新增买单量
'OrderVol_A1': order_vol_ask, # 假设挂在卖一价的新增卖单量
'CancelVol_B1': cancel_vol_bid,# 假设在买一价的撤单量
'CancelVol_A1': cancel_vol_ask # 假设在卖一价的撤单量
}
# --- LOB 特征计算与预测 ---
def calculate_lob_features(lob_snapshot, recent_activity):
"""
计算报告中提到的4个LOB衍生指标 (简化版)
"""
features = {}
# 1. SheetDiff: 静态订单簿失衡 (Level 1)
bid_vol = lob_snapshot['bid1_volume']
ask_vol = lob__snapshot['ask1_volume']
if (bid_vol + ask_vol) > 0:
features['SheetDiff'] = (bid_vol - ask_vol) / (bid_vol + ask_vol)
else:
features['SheetDiff'] = 0
# 计算总活动量用于归一化 (分母)
# 这里用的是B1和A1的总量,与报告公式定义略有简化
total_match_vol = recent_activity['MatchVol_B1'] + recent_activity['MatchVol_A1']
total_order_vol = recent_activity['OrderVol_B1'] + recent_activity['OrderVol_A1']
total_cancel_vol = recent_activity['CancelVol_B1'] + recent_activity['CancelVol_A1']
# 2. MatchDiff: 近期成交失衡 (Level 1)
if total_match_vol > 0:
# 注意: 报告原文是 MatchVol_B1 - MatchVol_A1,但 MatchVol_B1 通常指被动成交(主动卖),
# MatchVol_A1 通常指被动成交(主动买)。所以买方力量对应 A1 成交,卖方力量对应 B1 成交。
# 这里我们理解为 (主动买成交 - 主动卖成交) / 总成交
features['MatchDiff'] = (recent_activity['MatchVol_A1'] - recent_activity['MatchVol_B1']) / total_match_vol
else:
features['MatchDiff'] = 0
# 3. OrderDiff: 近期新增订单失衡 (Level 1)
if total_order_vol > 0:
features['OrderDiff'] = (recent_activity['OrderVol_B1'] - recent_activity['OrderVol_A1']) / total_order_vol
else:
features['OrderDiff'] = 0
# 4. CancelDiff: 近期撤单失衡 (Level 1)
if total_cancel_vol > 0:
# (买方撤单 - 卖方撤单) / 总撤单
features['CancelDiff'] = (recent_activity['CancelVol_B1'] - recent_activity['CancelVol_A1']) / total_cancel_vol
else:
features['CancelDiff'] = 0
return features
def predict_execution_prob(features):
"""
模拟基于LOB特征预测执行概率 (极其简化)
实际应用中需要加载训练好的模型 (如线性回归、GBDT、神经网络等)
这里仅用 SheetDiff 和 OrderDiff 做一个简单加权示意
返回一个分数,>0 表示利于买方限价单,<0 表示利于卖方限价单
"""
# 简化权重,仅作演示,不代表真实模型
weight_sheet = 0.6
weight_order = 0.3
weight_match = 0.05 # 通常成交信息更重要,但这里简化
weight_cancel = 0.05
prediction = (features.get('SheetDiff', 0) * weight_sheet +
features.get('OrderDiff', 0) * weight_order +
features.get('MatchDiff', 0) * weight_match + # 注意 MatchDiff 的符号含义
features.get('CancelDiff', 0) * weight_cancel)
# 添加一点随机噪声模拟预测不确定性
prediction += random.uniform(-0.05, 0.05)
return prediction
# --- 改进 TWAP 执行逻辑 ---
def improved_twap_executor(symbol, total_quantity, duration_minutes, interval_seconds=3, side='buy'):
"""
执行改进的TWAP策略
Args:
symbol (str): 标的代码
total_quantity (int): 总委托数量
duration_minutes (int): 执行总时长(分钟)
interval_seconds (int): 下单时间间隔(秒)
side (str): 'buy' 或 'sell'
"""
start_time = time.time()
end_time = start_time + duration_minutes * 60
n_intervals = int(duration_minutes * 60 / interval_seconds)
quantity_per_interval = round(total_quantity / n_intervals) if n_intervals > 0 else total_quantity
if quantity_per_interval <= 0:
print("Calculated quantity per interval is zero or negative. Check inputs.")
return
remaining_quantity = total_quantity
executed_quantity = 0
log = []
print(f"--- Starting Improved TWAP for {symbol} ---")
print(f"Target: {side} {total_quantity} shares over {duration_minutes} minutes.")
print(f"Interval: {interval_seconds}s, Quantity per interval: {quantity_per_interval}")
print("-" * 30)
current_interval = 0
while time.time() < end_time and remaining_quantity > 0:
current_interval += 1
interval_start_time = time.time()
# 1. 获取市场状态
lob_snapshot = get_simulated_lob_snapshot()
recent_activity = get_simulated_recent_activity(duration=interval_seconds) # 获取上个间隔的活动
# 2. 计算特征
features = calculate_lob_features(lob_snapshot, recent_activity)
# 3. 预测概率
prediction = predict_execution_prob(features)
# 4. 决定订单类型
order_type = 'UNKNOWN'
order_price = None
current_qty_to_trade = min(quantity_per_interval, remaining_quantity)
if side == 'buy':
if prediction > 0: # 预测有利于买方限价单成交
order_type = 'LIMIT'
order_price = lob_snapshot['bid1_price']
else:
order_type = 'MARKET'
order_price = lob_snapshot['ask1_price'] # 市价单对手价
elif side == 'sell':
if prediction < 0: # 预测有利于卖方限价单成交
order_type = 'LIMIT'
order_price = lob_snapshot['ask1_price']
else:
order_type = 'MARKET'
order_price = lob_snapshot['bid1_price'] # 市价单对手价
# 5. 模拟下单 (实际应调用交易API)
print(f"Interval {current_interval:03d}/{n_intervals} | Time: {time.strftime('%H:%M:%S')} | "
f"LOB: {lob_snapshot['bid1_price']:.2f}({lob_snapshot['bid1_volume']}) / "
f"{lob_snapshot['ask1_price']:.2f}({lob_snapshot['ask1_volume']}) | "
f"Pred: {prediction:.3f} | "
f"Decision: {side.upper()} {current_qty_to_trade} @ {order_type} "
f"{f'(Price: {order_price:.2f})' if order_type == 'LIMIT' else ''}")
# 模拟成交 (简化:假设订单都立刻成交了)
executed_quantity += current_qty_to_trade
remaining_quantity -= current_qty_to_trade
log_entry = {
'interval': current_interval,
'timestamp': interval_start_time,
'bid1_price': lob_snapshot['bid1_price'],
'bid1_volume': lob_snapshot['bid1_volume'],
'ask1_price': lob_snapshot['ask1_price'],
'ask1_volume': lob_snapshot['ask1_volume'],
'prediction': prediction,
'side': side,
'order_type': order_type,
'order_price': order_price if order_type == 'LIMIT' else None, # 记录限价单价格
'market_order_match_price': order_price if order_type == 'MARKET' else None, # 市价单成交价
'quantity': current_qty_to_trade,
'features': features # 记录用于决策的特征
}
log.append(log_entry)
# 等待下一个间隔
elapsed_time = time.time() - interval_start_time
sleep_time = interval_seconds - elapsed_time
if sleep_time > 0:
time.sleep(sleep_time)
print("-" * 30)
print(f"--- TWAP Execution Finished ---")
print(f"Total executed: {executed_quantity} / {total_quantity}")
print(f"Remaining: {remaining_quantity}")
# 分析执行日志 (例如,统计Limit/Market单比例,计算成本等)
log_df = pd.DataFrame(log)
if not log_df.empty:
limit_orders = log_df[log_df['order_type'] == 'LIMIT'].shape[0]
market_orders = log_df[log_df['order_type'] == 'MARKET'].shape[0]
total_orders = limit_orders + market_orders
if total_orders > 0:
print(f"Limit Order Ratio: {limit_orders / total_orders:.2%}")
print(f"Market Order Ratio: {market_orders / total_orders:.2%}")
return log_df
# --- 示例运行 ---
if __name__ == "__main__":
# 模拟参数
SYMBOL = "京东方A"
TOTAL_QUANTITY = 100000 # 总共买入10万股
DURATION_MINUTES = 30 # 在30分钟内完成
INTERVAL_SECONDS = 3 # 每3秒决策一次
SIDE = 'buy' # 买入
# 执行策略
execution_log = improved_twap_executor(SYMBOL, TOTAL_QUANTITY, DURATION_MINUTES, INTERVAL_SECONDS, SIDE)
# 打印部分日志
if execution_log is not None and not execution_log.empty:
print("\n--- Execution Log Sample (first 5 intervals) ---")
print(execution_log.head().to_string())
# 可以进一步分析 execution_log DataFrame 计算成交均价、滑点等指标
# 例如,计算平均成交价格 (需要有成交回报,这里简化了)
# avg_price = (log_df['quantity'] * log_df['market_order_match_price'].fillna(log_df['order_price'])).sum() / log_df['quantity'].sum()
# print(f"\nEstimated Average Execution Price (based on decision price): {avg_price:.3f}")
代码说明:
- 模拟环境:
get_simulated_lob_snapshot
和get_simulated_recent_activity
函数用于模拟市场数据,实际应用中需要替换为真实的数据接入和处理逻辑。 - 特征计算:
calculate_lob_features
根据报告中的定义(稍作简化和解释)计算了4个LOB衍生指标。 - 预测模型:
predict_execution_prob
是一个极其简化的预测函数,仅作演示。真实场景需要训练和加载一个基于历史数据得到的模型(如sklearn
的线性回归或更复杂的模型)。 - TWAP执行器:
improved_twap_executor
是核心逻辑:- 按设定的时间间隔循环。
- 在每个间隔开始时,获取市场状态、计算特征、进行预测。
- 根据预测结果和交易方向(买/卖)决定是下限价单(挂本方最优价)还是市价单。
- 记录决策日志,包括当时的LOB状态、特征值、预测值和订单类型。
- 模拟下单并更新剩余数量。
- 日志分析: 策略结束后,返回一个包含详细执行信息的 Pandas DataFrame,可以用于后续分析,如计算限价单/市价单比例、估计交易成本(需要更精确的成交回报信息)等。
重要提示:
- 简化: 此代码是教学性质的简化,省略了LOB构建、真实模型训练、交易接口对接、错误处理、成交确认等关键环节。
- 数据: 效果高度依赖于输入数据的质量和预测模型的准确性。模拟数据无法完全反映真实市场微观结构的复杂性。
- 模型:
predict_execution_prob
中的预测逻辑非常基础,实际需要根据历史数据训练更可靠的模型。 - 成本: 要评估策略效果,需要结合真实的成交回报数据计算滑点和交易成本。
这个复现提供了一个基于报告思想的框架,展示了如何利用LOB衍生指标动态调整TWAP策略中的订单类型。要在实盘中使用,还需要进行大量的数据处理、模型优化和系统工程工作。