第八讲:买卖价差分析

买卖价差,说白了就是你想买就得比想卖的人多付的那点钱。这玩意儿看着不起眼,但做高频交易的人都知道,它才是真正的「隐形杀手」。我刚开始做量化那会儿,总觉得价差就那么几个tick,能有多大影响?直到有一次回测跑得漂漂亮亮,实盘一上去直接亏成狗——嗯,后来才发现是没把价差算进去。

今天咱们就把价差拆开来看。它其实不是铁板一块,里面藏着三块成本:逆向选择成本、订单处理成本、存货持有成本。搞懂了这些,你就能预测价差走势,甚至找到套利机会。

核心观点:买卖价差 = 逆向选择成本 + 订单处理成本 + 存货持有成本。这三块各有各的脾气,拆开看才能对症下药。

价差分解模型:把大象装进冰箱

最早提出价差分解的是Roll(1984),他搞了个简单模型:

# Roll模型:价差 = 2 * sqrt(-Cov(ΔP_t, ΔP_{t-1}))
import numpy as np

def roll_spread(price_series):
    """计算Roll模型隐含价差"""
    returns = np.diff(price_series)
    cov = np.cov(returns[:-1], returns[1:])[0, 1]
    if cov < 0:
        spread = 2 * np.sqrt(-cov)
    else:
        spread = 0  # 协方差为正时模型失效
    return spread

这个模型有个硬伤——它假设价差是常数。你想想看,市场波动大的时候价差能一样吗?我当年用这个模型跑A股数据,结果发现大部分时间协方差都是正的,模型直接罢工。后来改用Huang & Stoll(1997)的分解模型,才真正把三块成本拆开。

Huang-Stoll模型的核心思路是:

  1. 逆向选择成本:信息不对称带来的损失。你下单的时候,可能对面坐着一个知道内幕的人。
  2. 订单处理成本:交易所收的手续费、系统延迟、人工成本这些「硬开销」。
  3. 存货持有成本:做市商手里囤着股票,价格波动带来的风险成本。
# Huang-Stoll模型简化实现
def huang_stoll_decomposition(trade_data):
    """
    输入:trade_data包含价格、买卖方向、成交量
    输出:三块成本的估计值
    """
    # 这里用OLS回归分解
    # 模型:ΔP_t = α + β1*Q_t + β2*Q_{t-1} + ε_t
    # 其中Q_t是买卖方向(+1买,-1卖)
    from statsmodels.api import OLS
    
    Q = trade_data['direction'].values
    dP = np.diff(trade_data['price'].values)
    
    X = np.column_stack([np.ones(len(Q)-1), Q[1:], Q[:-1]])
    model = OLS(dP[1:], X[1:]).fit()
    
    # 逆向选择成本 = β1的系数
    # 订单处理成本 = (β1 + β2) / 2
    # 存货持有成本 = (β1 - β2) / 2
    adverse_selection = model.params[1]
    order_processing = (model.params[1] + model.params[2]) / 2
    inventory_holding = (model.params[1] - model.params[2]) / 2
    
    return {
        'adverse_selection': adverse_selection,
        'order_processing': order_processing,
        'inventory_holding': inventory_holding
    }

避坑指南:我曾经用这个模型跑美股数据,发现逆向选择成本经常是负的。后来查资料才发现,高频交易环境下买卖方向的自相关会导致估计偏差。解决办法是用GMM(广义矩估计)替代OLS,或者把数据聚合到秒级再跑。

逆向选择成本:信息不对称的代价

说白了就是「你下单的时候,可能有人在偷看你的底牌」。做市商最怕这个——万一对面是个知情交易者,他一买价格就涨,一卖价格就跌,做市商两头挨打。

怎么量化这个成本?我常用的方法是看「价格冲击」:

def adverse_selection_measure(trades, quote_data):
    """
    用价格冲击法估算逆向选择成本
    """
    # 计算每笔交易后的价格变化
    trades['price_impact'] = trades['price'].shift(-1) - trades['price']
    
    # 按买卖方向分组
    buy_trades = trades[trades['direction'] == 1]
    sell_trades = trades[trades['direction'] == -1]
    
    # 逆向选择成本 = 买方冲击 - 卖方冲击
    buy_impact = buy_trades['price_impact'].mean()
    sell_impact = sell_trades['price_impact'].mean()
    
    return (buy_impact - sell_impact) / 2

你想想看,如果逆向选择成本很高,说明市场里「聪明钱」很多。这时候做市商会主动扩大价差来保护自己。我在2015年股灾期间观察过,当时很多股票的逆向选择成本直接翻了三倍,价差也跟着暴涨。

订单处理成本:硬开销不能省

这部分最实在——交易所手续费、结算费、系统维护费,每一笔都是真金白银。做市商不是做慈善的,这些成本必须从价差里赚回来。

怎么估算?有个简单办法:看最小价格变动单位(tick size)。如果tick size是0.01元,那订单处理成本至少是0.005元(一半的tick)。因为做市商不可能报出比tick size更小的价差。

def estimate_order_processing_cost(tick_size, trade_volume):
    """
    估算订单处理成本
    tick_size: 最小价格变动单位
    trade_volume: 平均每笔成交量
    """
    # 固定成本:交易所手续费 + 清算费
    fixed_cost = 0.0001  # 万分之一,假设值
    
    # 变动成本:与成交量相关
    variable_cost = 0.00005 * np.log(trade_volume + 1)
    
    # 总订单处理成本
    total_cost = tick_size / 2 + fixed_cost + variable_cost
    
    return total_cost

注意:订单处理成本在不同市场差异巨大。A股是T+1,做市商要考虑隔夜风险;美股是T+0,但SEC要收费用。我建议你跑实盘前,先跟券商把费率清单要过来,一项一项算清楚。

存货持有成本:囤货的风险溢价

做市商手里得有货才能做市。但囤货有风险——万一价格暴跌,库存就亏了。这个风险必须用价差来补偿。

存货持有成本跟三个因素有关:

  • 持仓时间:货拿得越久,风险越大
  • 波动率:波动越大,潜在亏损越大
  • 资金成本:借钱囤货要付利息
def inventory_holding_cost(volatility, holding_period, interest_rate):
    """
    计算存货持有成本
    volatility: 年化波动率
    holding_period: 平均持仓天数
    interest_rate: 年化资金成本
    """
    # 风险溢价 = 波动率 * sqrt(持仓时间/252)
    risk_premium = volatility * np.sqrt(holding_period / 252)
    
    # 资金成本 = 利率 * 持仓时间/365
    funding_cost = interest_rate * holding_period / 365
    
    return risk_premium + funding_cost

我记得有次做加密货币的做市策略,比特币波动率动不动就100%以上,存货持有成本高得吓人。最后我们不得不把持仓时间控制在几分钟以内,否则价差根本覆盖不了风险。

价差预测:从分解到实战

拆完了三块成本,咱们就能预测价差了。核心逻辑是:

  1. 监控逆向选择成本的变化(比如大单出现时)
  2. 估算订单处理成本(基本固定,但要注意交易所调费率)
  3. 动态计算存货持有成本(波动率飙升时要加价)
def predict_spread(quote_data, trade_data, volatility_model):
    """
    实时预测最优买卖价差
    """
    # 1. 逆向选择成本:用最近10笔交易的价格冲击估算
    recent_trades = trade_data.tail(10)
    adverse = adverse_selection_measure(recent_trades, quote_data)
    
    # 2. 订单处理成本:固定值
    order_proc = estimate_order_processing_cost(0.01, 1000)
    
    # 3. 存货持有成本:用GARCH模型预测未来波动率
    pred_vol = volatility_model.predict(5)  # 预测未来5分钟波动率
    inventory = inventory_holding_cost(pred_vol, 5/1440, 0.03)
    
    # 总价差 = 2 * (逆向选择 + 订单处理 + 存货持有)
    predicted_spread = 2 * (adverse + order_proc + inventory)
    
    return predicted_spread

实战技巧:我建议你把这个预测模型做成一个「价差仪表盘」。当预测价差明显大于当前价差时,说明做市商在「亏本做生意」,这时候进场做市有利可图。反过来,如果预测价差小于当前价差,说明价差偏高,应该等一等再下单。

知识体系总览

下面这张图把价差分析的整个逻辑串起来了。你可以把它当成一个检查清单,做策略的时候对着看,哪块没考虑到就补哪块。

买卖价差分析知识体系 买卖价差 逆向选择成本 订单处理成本 存货持有成本 价格冲击模型 信息不对称指标 Tick Size分析 费率结构计算 波动率预测 资金成本计算 价差预测 → 做市策略优化 三块成本动态变化,实时预测价差才能抓住交易机会

嗯,到这里价差分析的核心内容就讲完了。记住一句话:价差不是成本,是信息。拆开它,你就能看到市场里谁在赚钱、谁在亏钱、谁在承担风险。下次做策略的时候,别忘了先问问自己——我的价差模型把这三块成本都算进去了吗?

无相订单流研究社 微信Lucian808555