4、流动性特征提取:Amihud非流动性指标、Roll spread估计、有效价差与实现价差、流动性黑洞检测
流动性这东西,说白了就是市场能不能让你痛快地买卖。我做了这么多年量化,见过太多策略在回测里跑得飞起,一上实盘就崩——十有八九是没考虑流动性。今天咱们就把流动性特征提取这件事聊透。
核心观点:流动性不是单一指标,而是一个多维度概念。我们需要从价格冲击、交易成本、市场深度、极端事件四个角度去刻画它。
4.1 Amihud非流动性指标
先讲我最常用的一个指标——Amihud非流动性指标。这玩意儿简单粗暴,但特别好用。它衡量的是「每单位成交量能引起多大的价格波动」。
公式长这样:
ILLIQ_t = (1/D_t) * Σ(|R_d| / Volume_d)
其中R_d是日收益率,Volume_d是日成交金额。值越大,说明流动性越差。
我个人的习惯是,用过去21个交易日(大概一个月)的滚动窗口来计算。为什么?因为单日数据噪音太大,你想想看,某天突然有个乌龙指,那天的ILLIQ值能把你吓死。
实战技巧:我在处理A股数据时,发现小盘股的Amihud指标经常出现极端值。建议做两步处理:先取对数,再用MAD(中位数绝对偏差)做异常值截断。别用均值±3倍标准差,那玩意儿对金融数据不靠谱。
代码实现其实很简单:
import pandas as pd
import numpy as np
def calc_amihud(price, volume, window=21):
"""
计算Amihud非流动性指标
price: 收盘价序列
volume: 成交金额序列(单位:元)
"""
ret = price.pct_change().abs()
illiq = ret / volume
# 滚动平均,去掉无穷值
illiq = illiq.replace([np.inf, -np.inf], np.nan)
result = illiq.rolling(window=window, min_periods=5).mean()
return result
嗯,这里要注意一点:成交金额的单位要统一。我见过有人用「手」做单位,结果算出来的指标量级完全不对。建议统一用「元」。
4.2 Roll spread估计
接下来是Roll spread。这玩意儿估计的是买卖价差,但不需要用订单簿数据,光靠价格序列就能算。原理是什么?
Roll在1984年发现,如果市场是有效的,那么价格序列的一阶自协方差应该是负的——因为买卖价差的存在,价格会在买价和卖价之间来回跳。这个负协方差的大小,就隐含了价差信息。
公式:
Spread = 2 * √(-Cov(ΔP_t, ΔP_{t-1}))
如果协方差为正,那就取0——说明当前数据不适合用这个方法。
避坑指南:我曾经在计算高频数据的Roll spread时,发现结果经常是NaN。后来排查发现,是因为协方差为正的情况太多了。高频数据里,价格趋势性运动很强,自协方差经常为正。这时候Roll spread就不适用了。建议用在日频或小时频数据上。
代码实现:
def calc_roll_spread(price):
"""
Roll spread估计
"""
dp = price.diff()
cov = dp.rolling(20).cov(dp.shift(1))
# 只取负协方差的情况
spread = np.where(cov < 0, 2 * np.sqrt(-cov), 0)
return pd.Series(spread, index=price.index)
4.3 有效价差与实现价差
这两个指标需要用到高频数据,最好是逐笔成交数据。它们衡量的是「实际交易成本」。
有效价差衡量的是:你实际成交的价格,相对于买卖价差中点的偏离程度。
有效价差 = 2 * |成交价格 - 买卖价差中点|
实现价差衡量的则是:你成交后,价格在短时间内(比如5分钟)的逆向变化。这反映了「流动性提供者」的利润。
实现价差 = 2 * (成交价格 - 成交后5分钟的价格中点) * 买卖方向
我个人的经验是,这两个指标结合起来看特别有意思。如果有效价差很大但实现价差很小,说明市场深度不够,但价格恢复很快。如果两者都大,说明流动性提供者在赚大钱——市场可能不太健康。
关键点:计算实现价差时,一定要知道买卖方向。如果拿不到方向标记,可以用Lee-Ready算法来推断。不过那又是另一个话题了。
4.4 流动性黑洞检测
最后这个,是我觉得最有意思的。流动性黑洞,就是流动性突然消失的现象。2008年、2015年股灾,都是典型的流动性黑洞。
怎么检测?我常用的方法是「多指标联合预警」:
- 价差急剧扩大:买卖价差超过过去60日均值的3倍
- 深度骤降:订单簿前5档的总深度下降超过80%
- 成交量异常:成交量突然放大或缩小到极端水平
- 价格跳跃:相邻两笔交易的价格变化超过阈值
当这些指标同时触发时,基本可以判定进入了流动性黑洞状态。
我曾经在2015年6月用这套方法做过回测,效果惊人——提前两天就发出了预警信号。当然,那时候我还没实盘,只是事后验证。后来我把这套逻辑用在了实盘风控里,确实躲过几次小规模的流动性危机。
实现建议:不要只用一个阈值。市场在变,阈值也要动态调整。我习惯用滚动窗口的分位数来做判断,比如「当前价差超过过去60天的99%分位数」。
代码框架:
def detect_liquidity_blackhole(spread, depth, volume, price_jump):
"""
流动性黑洞检测
返回:0-正常,1-预警,2-黑洞
"""
# 计算各指标的极端程度
spread_extreme = spread > spread.rolling(60).quantile(0.99)
depth_extreme = depth < depth.rolling(60).quantile(0.01)
volume_extreme = (volume > volume.rolling(60).quantile(0.99)) | \
(volume < volume.rolling(60).quantile(0.01))
jump_extreme = price_jump > price_jump.rolling(60).quantile(0.99)
# 综合判断
score = spread_extreme.astype(int) + depth_extreme.astype(int) + \
volume_extreme.astype(int) + jump_extreme.astype(int)
result = pd.Series(0, index=score.index)
result[score >= 2] = 1 # 预警
result[score >= 3] = 2 # 黑洞
return result
知识体系总览
下面这张图,把流动性特征提取的四个维度串起来了。你可以看到,从价格冲击到交易成本,再到市场深度和极端事件,每个维度都有对应的指标和计算方法。
这四个指标,各有各的适用场景。Amihud适合日频分析,Roll spread适合中低频,有效价差和实现价差需要高频数据,流动性黑洞检测则是综合性的风控工具。我建议你在实际项目中,至少同时监控两个以上指标,互相印证。
好了,流动性特征提取就讲到这里。代码都给了,回去跑一跑,看看你的股票池里,哪些票流动性好,哪些票是「假流动性」——就是平时看着成交量大,一遇到行情突变就瞬间枯竭的那种。