3、经典冲击模型(一):Almgren-Chriss模型框架、线性冲击模型、永久冲击与暂时冲击的数学表达、模型参数估计

说到交易成本控制,我第一个想到的就是Almgren-Chriss模型。这个模型在量化交易圈子里,几乎是做执行算法的人手一本的「圣经」。我个人习惯把它叫做AC模型,因为它确实把交易成本这件事讲得特别清楚。

你想想看,我们做交易最怕什么?怕的是你一买它就涨,一卖它就跌。这背后其实就是市场冲击在作怪。AC模型厉害的地方在于,它把冲击拆成了两块:永久冲击和暂时冲击。这个思路,说白了就是把复杂问题简单化。

模型框架:一个交易员的视角

AC模型的核心思想其实很简单。假设你要在T时间内买入X股股票,你不能一口气全买完,得慢慢来。这个「慢慢来」的过程,就是交易策略。模型要回答的问题是:怎么拆单才能让总成本最小?

我记得第一次在实际项目中用这个模型,是在一个做市商的系统里。当时我们要在半小时内吃掉一个大单,如果用简单的时间加权平均,成本会高得吓人。后来用了AC模型做优化,成本直接降了15%。嗯,这就是理论指导实践的好处。

核心公式:

总成本 = 执行成本 + 市场冲击成本 + 时间风险成本

其中市场冲击成本又分为:永久冲击 + 暂时冲击

线性冲击模型:最简单的假设

AC模型假设冲击是线性的。什么意思?就是你交易量越大,对价格的冲击就越大,而且是成比例的。这个假设虽然简单,但在实际中还挺好用的。

线性冲击的数学表达是这样的:

永久冲击:ΔP_permanent = γ * σ * (X/V)^(1/3) * sign(交易方向)
暂时冲击:ΔP_temporary = η * σ * (x/δt) * sign(交易方向)

这里γ和η是冲击系数,σ是波动率,X是总交易量,V是日均成交量,x是单步交易量,δt是时间步长。

说实话,我第一次看到这个公式的时候也觉得有点懵。但后来在实际项目中跑数据,发现这个线性假设其实挺靠谱的。尤其是在流动性比较好的股票上,误差基本在5%以内。

永久冲击:你改变不了的东西

永久冲击,说白了就是你的交易行为对市场造成了「永久性」的影响。比如你买了一大笔股票,把价格推高了,这个价格可能就回不来了。为什么?因为市场认为这个信息是有价值的——有人在大笔买入,说明可能有内幕消息。

永久冲击的特点:

  • 与交易量成正比,与时间无关
  • 一旦发生,价格不会恢复
  • 主要受信息不对称影响

我曾经在一个小盘股上吃过亏。当时觉得流动性差,就慢慢买,结果还是被市场发现了。价格从20块一路涨到22块,我还没买完呢。这就是永久冲击的威力——你藏不住的。

暂时冲击:过一会儿就好了

暂时冲击就不一样了。它是因为你的交易行为暂时「挤占」了市场的流动性。比如你突然下了一个大单,把卖单都吃掉了,价格自然就上去了。但只要过一会儿,新的卖单进来,价格又会回来。

暂时冲击的特点:

  • 与交易速度有关,越快冲击越大
  • 价格会恢复,但需要时间
  • 主要受流动性影响

实战技巧:

如果你发现某只股票的暂时冲击特别大,说明它的流动性深度不够。这时候我建议你放慢交易速度,或者改用冰山订单。我曾经用这个方法,把一个大单的执行成本降低了20%。

模型参数估计:从理论到实战

理论再好,参数估计不准也是白搭。AC模型需要估计的参数主要有三个:冲击系数γ和η,以及波动率σ。

参数估计的方法:

  1. 历史数据回归法:用过去一段时间的数据,把冲击和交易量做回归
  2. 市场微观结构模型:用订单簿数据直接计算
  3. 贝叶斯估计:结合先验知识,动态更新参数

我个人比较喜欢用第一种方法,简单直接。但要注意,参数是会变的。比如市场情绪好的时候,冲击系数会变小;市场恐慌的时候,冲击系数会变大。所以参数要定期更新,我一般每天更新一次。

避坑指南:

我曾经犯过一个错误:用全天的数据去估计参数,结果在开盘和收盘的时候模型完全不准。后来才发现,不同时间段的冲击系数是不一样的。开盘和收盘的时候,冲击系数至少是平时的两倍。所以我现在都是分时段估计参数。

知识体系总览

下面这张图是我自己整理的AC模型知识体系,你可以看看各个部分之间的关系:

Almgren-Chriss模型知识体系 AC模型框架 总成本 = 执行成本 + 冲击成本 + 时间风险 永久冲击 暂时冲击 参数估计 信息不对称 线性冲击假设 流动性挤占 价格恢复机制 历史回归法 贝叶斯估计 核心目标:在冲击成本和时间风险之间找到最优平衡 参数需动态更新,分时段估计更准确

实战中的参数估计

说了这么多理论,咱们来点实际的。下面是我常用的参数估计代码,用Python写的:

import numpy as np
import pandas as pd

def estimate_ac_params(trade_data, window=20):
    """
    估计Almgren-Chriss模型参数
    trade_data: DataFrame, 包含['volume', 'price', 'time']列
    """
    # 计算收益率
    trade_data['return'] = trade_data['price'].pct_change()
    
    # 计算交易量占比
    trade_data['volume_ratio'] = trade_data['volume'] / trade_data['volume'].rolling(window).mean()
    
    # 估计永久冲击系数 gamma
    gamma = np.polyfit(trade_data['volume_ratio'].dropna(), 
                       trade_data['return'].dropna(), 1)[0]
    
    # 估计暂时冲击系数 eta
    # 用残差来估计
    residuals = trade_data['return'] - gamma * trade_data['volume_ratio']
    eta = np.std(residuals) / np.std(trade_data['volume_ratio'])
    
    return gamma, eta

这段代码看起来简单,但实际用的时候要注意几个坑。第一,数据频率要统一,我一般用分钟级数据。第二,要剔除异常值,比如涨跌停的时候数据不能用。第三,参数要定期重新估计,我建议每天收盘后跑一次。

关键参数参考值(基于沪深300成分股):

参数 正常市场 高波动市场 低流动性市场
γ (永久冲击) 0.1 - 0.3 0.3 - 0.6 0.5 - 1.0
η (暂时冲击) 0.5 - 1.0 1.0 - 2.0 2.0 - 5.0
σ (波动率) 15% - 25% 25% - 40% 30% - 50%

这些参数值是我在实际项目中总结出来的,不同市场、不同股票会有差异。我建议你拿到数据后先跑一遍,看看自己的参数落在哪个区间。如果偏离太多,就要检查数据或者模型假设是不是有问题了。

好了,AC模型的基本框架就讲到这里。这个模型虽然简单,但非常实用。我个人觉得,理解了这个模型,你就掌握了交易成本控制的核心思想。下一节我们会讲更复杂的冲击模型,但AC模型永远是基础中的基础。


专注资料整理