22. 市场冲击模型:Almgren-Chriss模型,大单拆单算法

做量化交易的朋友,尤其是做高频或者程序化套利的,一定都遇到过这个灵魂拷问:「我的大单子怎么一进去,价格就跑了?」

嗯,这就是市场冲击。说白了,你下单本身就在改变市场。你买,价格涨;你卖,价格跌。对于小资金可能无所谓,但做基差套利,尤其是跨期、跨品种套利,仓位一重,冲击成本直接吃掉你大部分利润。

我个人习惯,在实盘前一定会用Almgren-Chriss模型跑一遍冲击成本预估。今天我们就来拆解这个经典模型,以及它背后的拆单算法。

22.1 市场冲击的两种成分

先理清概念。市场冲击不是铁板一块,它分两块:

  • 永久冲击(Permanent Impact):你买完,价格回不去了。这反映了市场吸收了你的订单,信息被消化了。
  • 临时冲击(Temporary Impact):你买的时候价格被推高,但过一会儿流动性恢复,价格又回落。这部分是「过路费」。

Almgren-Chriss模型的核心,就是把这两部分分开建模。为什么?因为永久冲击你躲不掉,但临时冲击可以通过拆单来优化。

核心公式(简化版):

价格变化 = 永久冲击项 + 临时冲击项

ΔS(t) = γ * v(t) + η * σ * sign(v(t))

其中:γ是永久冲击系数,v(t)是交易速率,η是临时冲击系数,σ是波动率。

我在项目中遇到过最典型的案例:某次做螺纹钢跨期套利,一次性挂了200手买单,结果盘口瞬间被打穿三个tick,成交均价直接比预期高了0.5%。后来用Almgren-Chriss模型一算,发现临时冲击占了总成本的70%。

22.2 大单拆单:从「一锤子买卖」到「细水长流」

既然大单直接砸会引发冲击,那我们就把它拆成小单,分批进场。这就是拆单算法的基本思想。

Almgren-Chriss模型给出了一个最优拆单路径:在「快速完成」和「减少冲击」之间找平衡

你想想看,如果你拆得太碎,时间拉长,价格波动的风险就大了。如果你一把梭,冲击成本又高。所以这是个典型的优化问题。

我的经验: 对于日内套利策略,我一般把拆单时间窗口设在5-15分钟。太短了冲击降不下来,太长了基差可能已经变了。

22.3 Almgren-Chriss模型的数学框架

这里我尽量不堆公式,但核心逻辑你得懂。

模型假设你要在时间T内完成总数量Q的订单。你选择一条交易速率曲线v(t),使得总成本最小化:

总成本 = 永久冲击成本 + 临时冲击成本 + 风险成本

其中风险成本来自你持仓期间的价格波动。模型解出来,最优策略是:前期交易速率快,后期逐渐减慢。为什么?因为前期你仓位轻,波动风险小,可以多交易;后期仓位重,要减少暴露。

嗯,这里要注意:这个结论的前提是价格服从随机游走。如果你的品种有趋势性,那策略就要调整了。

22.4 代码实现:一个简单的拆单模拟器

光说不练假把式。我写了一个简化版的Almgren-Chriss拆单模拟,帮你直观感受一下。

import numpy as np
import matplotlib.pyplot as plt

class AlmgrenChriss:
    def __init__(self, Q, T, sigma, gamma, eta):
        self.Q = Q          # 总订单量
        self.T = T          # 总时间
        self.sigma = sigma  # 波动率
        self.gamma = gamma  # 永久冲击系数
        self.eta = eta      # 临时冲击系数
        
    def optimal_trajectory(self, N=100):
        """计算最优交易路径"""
        dt = self.T / N
        t = np.linspace(0, self.T, N)
        
        # 最优解:交易速率随时间递减
        # 这里用简化版:线性递减
        v = np.linspace(2*self.Q/self.T, 0, N)
        
        # 累计成交量
        x = np.cumsum(v * dt)
        
        # 价格冲击
        perm_impact = self.gamma * v
        temp_impact = self.eta * self.sigma * np.sign(v)
        total_impact = perm_impact + temp_impact
        
        return t, x, v, total_impact

# 使用示例
model = AlmgrenChriss(Q=1000, T=600, sigma=0.02, gamma=0.001, eta=0.005)
t, x, v, impact = model.optimal_trajectory()

print(f"最终成交量: {x[-1]:.0f}")
print(f"最大冲击成本: {np.max(impact):.4f}")

避坑指南: 我曾经在实盘里直接用这个模型,结果发现冲击系数γ和η根本不准。后来才意识到,不同品种、不同时间段的流动性差异巨大。建议你至少用一周的历史数据来校准参数,别直接用默认值。

22.5 知识体系图:Almgren-Chriss模型全流程

下面这张图帮你理清整个逻辑链条:

Almgren-Chriss模型全流程 输入参数 订单量Q | 时间T | 波动率σ 市场冲击模型 永久冲击 + 临时冲击 成本优化 冲击+风险最小化 最优拆单策略 交易速率v(t)随时间递减 执行与监控 实时跟踪 | 动态调整 | 冲击成本核算 参数校准反馈 核心思想:在冲击成本与持仓风险之间寻找最优平衡点 参数γ、η需根据品种和时段动态校准

22.6 实战中的几个关键点

模型讲完了,说几个实战中容易踩的坑:

  1. 参数校准是灵魂:γ和η不是固定的。我习惯每天开盘前用前5分钟的数据重新算一遍。
  2. 别忘了滑点:Almgren-Chriss模型假设你能按计算价格成交,但实际还有买卖价差。建议在冲击成本上加一个buffer。
  3. 拆单粒度要合适:拆得太细,交易次数太多,手续费和延迟成本反而上升。我一般控制在每单不超过总订单量的5%。
  4. 市场状态要识别:如果盘口深度突然变薄,或者波动率飙升,模型参数要立刻切换。我曾经在数据发布前没调整参数,结果冲击成本翻了三倍。

一个小技巧: 对于基差套利,你可以把两个腿的冲击成本分开算。比如做多近月、做空远月,近月流动性好,冲击系数小;远月流动性差,冲击系数大。这样拆单时,远月可以拆得更碎一些。

22.7 总结

Almgren-Chriss模型不是银弹,但它给了我们一个系统性的框架去思考大单执行问题。说白了,它把「怎么下单」这件事从艺术变成了科学。

我个人建议,刚开始做套利的朋友,先别急着上复杂模型。用这个简化版跑一段时间,积累一些冲击成本的历史数据,慢慢你就能找到自己品种的「手感」了。

嗯,今天就到这里。记住:冲击成本是套利者的隐形杀手,别让它悄悄吃掉你的利润。


公众号:蓝海资料掘金营,微信deep3321