2、市场微观结构:订单簿、买卖价差、交易量分布、市场冲击成本

好,咱们进入第二章。这一章聊的是市场微观结构,说白了就是「市场到底是怎么运作的」。很多人做量化,上来就撸因子、跑回归,结果实盘一跑就崩。为什么?因为忽略了市场的底层逻辑。

我个人习惯,做任何策略之前,先花一周时间把微观结构摸透。你想想看,连订单簿长什么样都不知道,你怎么敢说自己懂Alpha?

2.1 订单簿:市场的「实时心电图」

订单簿,英文叫Order Book。它记录了所有未成交的买单和卖单。每一笔交易,本质上都是订单簿上的「撮合」。

我举个例子。假设某只股票当前买一价是10.00元,卖一价是10.01元。你想买入100股,如果直接吃卖一,成交价就是10.01。如果你挂10.00的限价单,那就得等别人卖给你。

订单簿的核心结构很简单:

  • 买盘(Bid):从高到低排列,买一最高
  • 卖盘(Ask):从低到高排列,卖一最低
  • 深度(Depth):每个价位上的挂单量

嗯,这里要注意。订单簿不是静态的。它每时每刻都在变化。高频交易者就是靠捕捉订单簿的「瞬间失衡」来赚钱的。

关键点:订单簿的斜率变化,往往预示着价格即将突破。我在项目中遇到过,当买盘深度突然变厚,而卖盘深度不变时,价格大概率会向上走。

咱们用Python拉一下订单簿数据,看看长什么样:

import pandas as pd
import numpy as np

# 模拟一个简单的订单簿
def generate_order_book(mid_price=100.0, spread=0.02, depth=10):
    bids = []
    asks = []
    for i in range(depth):
        bid_price = mid_price - spread/2 - i * 0.01
        ask_price = mid_price + spread/2 + i * 0.01
        bid_vol = np.random.randint(100, 1000)
        ask_vol = np.random.randint(100, 1000)
        bids.append([bid_price, bid_vol])
        asks.append([ask_price, ask_vol])
    return pd.DataFrame(bids, columns=['price', 'volume']), pd.DataFrame(asks, columns=['price', 'volume'])

bids, asks = generate_order_book()
print("买盘前5档:")
print(bids.head())
print("\n卖盘前5档:")
print(asks.head())

这段代码模拟了10档深度。实际交易中,你看到的Level 2数据就是这种结构。

2.2 买卖价差:你交易的成本「隐形杀手」

买卖价差(Bid-Ask Spread),就是买一和卖一之间的差价。它直接决定了你的交易成本。

举个例子。你看到某股票买一10.00,卖一10.02。价差是0.02元。如果你现在买入,成交价是10.02。如果你立刻卖出,成交价是10.00。一进一出,你就亏了0.02元。这就是价差成本。

价差的大小,反映了市场的流动性。流动性好的股票,价差可能只有1分钱。流动性差的股票,价差可能几毛钱甚至几块钱。

我曾经踩过一个坑。做回测时,我假设以收盘价成交。结果实盘一跑,发现每次交易都多亏了0.5%的价差。半年下来,收益直接腰斩。从那以后,我再也不敢忽略价差了。

实战技巧:计算有效价差时,别只看买一卖一。我习惯用「成交量加权平均价差」,这样更贴近实际成交成本。

价差的计算公式很简单:

spread = (ask_price - bid_price) / mid_price * 10000  # 单位:基点

比如价差0.02,中间价10.01,那么价差就是 (0.02 / 10.01) * 10000 ≈ 20个基点。嗯,20个基点是什么概念?如果你每天交易一次,一年250个交易日,光价差成本就吃掉50%的收益。

2.3 交易量分布:谁在买?谁在卖?

交易量分布,说白了就是「钱往哪儿流」。它包含两个维度:时间分布和价格分布。

时间分布:一天中,交易量并不是均匀的。开盘和收盘时交易量最大,午盘相对清淡。为什么?因为开盘时信息集中释放,收盘时机构调仓。

价格分布:某个价格区间内,成交了多少量。这其实就是Volume Profile(成交量分布图)。

我个人习惯,用Volume Profile来识别「价值区域」。如果价格跌破了价值区域的下沿,我会考虑做多。反之亦然。

咱们画一下交易量的时间分布:

import matplotlib.pyplot as plt
import numpy as np

# 模拟一天内的交易量分布
minutes = np.arange(0, 240)  # 4小时交易时间
volume = np.zeros(240)
# 开盘和收盘放量
volume[:30] = np.random.uniform(100, 200, 30)
volume[30:210] = np.random.uniform(50, 100, 180)
volume[210:] = np.random.uniform(100, 200, 30)

plt.figure(figsize=(10, 4))
plt.plot(minutes, volume)
plt.title('日内交易量分布(模拟)')
plt.xlabel('分钟')
plt.ylabel('成交量')
plt.grid(True, alpha=0.3)
plt.show()

你看,这个U型分布很典型。如果你做的是高频策略,最好避开午盘那段时间,流动性太差,滑点大。

注意:别把交易量分布当成静态的。市场环境变了,分布也会变。比如突发新闻时,交易量会瞬间爆发。我建议每周重新计算一次分布参数。

2.4 市场冲击成本:大单的「隐形税」

市场冲击成本,就是你下单时「推动价格」造成的额外成本。你想想看,如果你要买100万股,而买一只有1000股,那你只能一路往上吃,把价格推高。这个推高的部分,就是冲击成本。

冲击成本的计算,业界常用Almgren-Chriss模型。核心公式是:

Impact = k * sigma * (Q / V)^alpha

其中:

  • k:冲击系数,一般取0.1~0.5
  • sigma:波动率
  • Q:你的交易量
  • V:市场总成交量
  • alpha:指数,一般取0.5~1.0

举个例子。某股票日成交量100万股,波动率20%。你想买1万股。假设k=0.2,alpha=0.5,那么冲击成本就是:

Impact = 0.2 * 0.2 * (10000 / 1000000)^0.5
       = 0.04 * 0.1
       = 0.004  # 即0.4%

也就是说,你这一单下去,价格会被推高0.4%。如果你做的是高频策略,0.4%的冲击成本足以让策略从盈利变成亏损。

我曾经帮一个客户优化算法交易。他原来的策略是直接市价单全仓买入,冲击成本高达1.2%。我建议他改用TWAP(时间加权平均价格)算法,把大单拆成小单,分批下单。结果冲击成本降到了0.3%。

核心结论:冲击成本是Alpha的天敌。你辛辛苦苦挖出来的因子,可能赚了0.5%的收益,结果冲击成本吃掉0.8%。所以,做策略时一定要把冲击成本算进去。

2.5 知识体系总览

说了这么多,咱们用一张图把这一章的核心逻辑串起来:

市场微观结构 订单簿 买卖价差 交易量分布 市场冲击成本 核心要素 • 买盘/卖盘排列 • 深度与斜率 • 动态变化捕捉 核心要素 • 流动性度量 • 有效价差计算 • 交易成本估算 核心要素 • 时间分布(U型) • 价格分布(VP) • 价值区域识别 核心要素 • Almgren-Chriss模型 • 冲击系数估算 • 算法交易拆单 理解微观结构 → 降低交易成本 → 提升Alpha纯度

这张图把四个核心概念串起来了。订单簿是基础,价差和交易量分布是中间层,冲击成本是最终要解决的问题。你想想看,如果不理解这些,你挖出来的Alpha很可能被交易成本吃光。

我的建议:做策略之前,先花一周时间跑一下订单簿数据。看看你的标的物,价差有多大?冲击成本有多高?交易量分布是什么样的?这些数据,比任何因子都重要。

好了,这一章就到这里。记住一句话:市场微观结构,是Alpha的「土壤」。土壤不好,种子再好也长不出庄稼。

专注资料整理