市场微观结构与订单簿基础
直觉
前面所有模块都假设「想买就能按收盘价买到」——这是研究者的视角。实盘里,你的订单要进入限价订单簿(LOB):买单和卖单排队匹配。买得少,按最便宜的卖价成交;买得多,就要一层层吃掉卖盘,越买越贵——这就是价量冲击(price impact)。微观结构研究的就是「订单如何在真实簿子里成交、并对价格造成多大冲击」,它是连接「理论信号」与「可实现的 PnL」的最后一公里。
订单簿、买卖价差与冲击
- 中间价 ;买卖价差 ;
- 市价单吃对手盘(提供流动性者付钱给你?不——你付价差),限价单挂单排队赚价差;
- 价量冲击(Kyle 线性模型):,订单量 越大、价格滑点越大, 是「市场深度」的倒数。
「人话」解释:市价单为什么「越买越贵」?
簿子里最便宜的卖价只有那么几股。你下一个 100 股的小单,按最便宜卖价成交;但下一个 10000 股的大单,便宜的卖盘被吃光后,系统会自动匹配更贵的下一档、再下一档……你的平均成交价(VWAP)就被越抬越高。这多付的部分就是冲击成本,是回测里最容易被忽略、实盘里最真实存在的损耗。流动性差的小盘股 大,同样金额冲击贵得多——这正是为什么很多策略在标普大盘上有效、下到小盘股就消失。
可运行案例:市价单的价量冲击
构造一个对称订单簿快照(每档固定深度),模拟买入不同数量时的 VWAP 滑点——直观看到「单子越大,滑点凸性上升」。
import numpy as np
import matplotlib.pyplot as plt
mid, tick, levels, depth = 100.0, 0.01, 20, 300
asks = [mid + (i+1)*tick for i in range(levels)] # 卖盘: 越往上越贵
ask_sizes = [depth]*levels # 每档深度(股)
def buy_vwap(q): # 市价买入 q 股, 逐档吃卖盘
rem, spent = q, 0.0
for px, sz in zip(asks, ask_sizes):
take = min(rem, sz); spent += take*px; rem -= take
if rem <= 0: break
if rem > 0: return None # 簿子不够深
return spent / q
print(f"有效价差(吃第1档) = {(asks[0]-mid)/mid:.4%}")
sizes = np.arange(0, 6000, 300)
slip = [(buy_vwap(q)-mid)/mid*1e4 for q in sizes] # 滑点(bps)
print(f"买 300 股 滑点={slip[1]:.2f}bps 买 6000 股 滑点={slip[-1]:.2f}bps")
plt.figure(figsize=(9, 3.4))
plt.plot(sizes, slip, color='darkorange', lw=1.6)
plt.xlabel('市价买单股数'); plt.ylabel('滑点 (bps)')
plt.title('价量冲击:订单越大,VWAP 滑点凸性上升'); plt.tight_layout(); plt.show()
print("\n→ 回测若假设按中间价成交, 会系统性高估大单策略的真实 PnL。")
小结
- 订单在限价订单簿里匹配:中间价、买卖价差、档位深度;
- 市价单付价差 + 吃深度的冲击,量越大滑点凸性越高();
- 策略落地必须把冲击成本算进去——这是研究与实盘 PnL 的主要落差来源之一。