4、流动性度量模型:Amihud非流动性指标、Roll价差估计量、流动性比率、价格影响模型
流动性这东西,说起来玄乎,其实就一句话:你想卖的时候,能不能快速卖掉,还不亏太多钱。做LOF套利,流动性就是你的命。我见过太多人策略跑得漂亮,一进场就被流动性卡死,那叫一个憋屈。
今天咱们就把流动性度量这件事彻底讲透。四个模型,一个比一个实用。你想想看,如果你连自己交易的品种流动性好坏都说不清楚,那套利不就是闭着眼过马路吗?
4.1 Amihud非流动性指标:最经典的流动性度量
Amihud指标,圈内人都叫它「非流动性指标」。名字听着绕口,逻辑其实特简单:每单位成交金额能引起多大的价格变动。价格变动越大,流动性越差。
公式长这样:
Amihud_illiq = mean( |r_t| / volume_t )
其中 r_t 是第 t 分钟的收益率,volume_t 是第 t 分钟的成交金额。取绝对值,再取平均。
我个人习惯用分钟级数据算这个指标。为什么?因为日线数据太粗糙了,LOF套利很多时候是分钟级甚至秒级的博弈,你用日线算出来的Amihud,基本等于没算。
实战经验:我在做LOF折溢价套利回测时,发现一个规律——Amihud指标超过0.5的品种,基本不适合做高频套利。你进去容易,出来难,滑点能把利润全吃掉。
代码实现也不复杂:
import pandas as pd
import numpy as np
def calc_amihud(df, price_col='close', vol_col='amount'):
"""
计算Amihud非流动性指标
df: 包含价格和成交金额的DataFrame
"""
df = df.copy()
df['return'] = df[price_col].pct_change().abs()
df['amihud'] = df['return'] / df[vol_col]
# 去掉无穷值和空值
df = df.replace([np.inf, -np.inf], np.nan).dropna()
return df['amihud'].mean()
# 用法示例
# amihud_value = calc_amihud(minute_data)
# print(f'Amihud非流动性指标: {amihud_value:.6f}')
嗯,这里要注意:如果成交金额为0,分母会出问题。我建议先过滤掉成交量为0的分钟数据,不然算出来全是inf,没意义。
4.2 Roll价差估计量:没有订单簿也能算价差
做套利的人都知道买卖价差有多重要。但问题来了——很多LOF品种的订单簿数据拿不到,或者拿到的质量很差。这时候怎么办?
Roll价差估计量就是来救场的。它只需要价格序列,就能估算出隐含的买卖价差。原理是基于价格序列的负自相关性。
公式:
Roll_spread = 2 * sqrt( -cov(ΔP_t, ΔP_{t-1}) )
如果协方差为正,说明当前数据不适合用Roll估计,直接返回0。
我曾经在做一个跨境LOF的流动性分析时,发现Roll价差算出来是负的。当时我还以为自己代码写错了,查了半天才发现——那个品种当天有分红除权,价格序列被破坏了。所以用Roll之前,一定要先做价格复权处理。
def calc_roll_spread(prices):
"""
Roll价差估计量
prices: 价格序列,Series或list
"""
price_series = pd.Series(prices)
delta = price_series.diff().dropna()
cov = delta.shift(1).cov(delta)
if cov < 0:
return 2 * np.sqrt(-cov)
else:
return 0.0
# 用法
# spread = calc_roll_spread(df['close'])
# print(f'Roll估计价差: {spread:.4f}')
小技巧:Roll价差对数据频率很敏感。我个人建议用5分钟或15分钟数据,太短了噪声大,太长了又失去时效性。做LOF套利,15分钟是个不错的折中点。
4.3 流动性比率:简单粗暴但有效
流动性比率(Liquidity Ratio)是我最早接触的流动性指标。说白了就是:一定价格变动范围内,能成交多少金额。
公式:
LR = 累计成交金额 / 价格变动百分比
价格变动越小,成交金额越大,流动性越好。这个指标的好处是直观,你给老板汇报的时候,不用解释太多。
但有个坑——价格变动为0的时候怎么办?分母不能为0啊。我一般会加一个极小值,比如1e-8,或者干脆跳过价格没变动的那些时间段。
def calc_liquidity_ratio(df, price_col='close', vol_col='amount', window=20):
"""
计算流动性比率
window: 滚动窗口期数
"""
df = df.copy()
df['price_change_pct'] = df[price_col].pct_change().abs()
# 防止除零
df['price_change_pct'] = df['price_change_pct'].replace(0, np.nan)
df['lr'] = df[vol_col].rolling(window).sum() / df['price_change_pct'].rolling(window).sum()
return df['lr'].dropna()
# 用法
# lr_series = calc_liquidity_ratio(minute_data)
# print(f'流动性比率均值: {lr_series.mean():.2f}')
避坑指南:我曾经用流动性比率筛选套利标的,结果选出来的全是货币基金。为什么?因为货币基金价格几乎不动,分母趋近于0,LR算出来巨大。所以这个指标更适合价格波动正常的品种,对价格僵硬的品种要慎用。
4.4 价格影响模型:量化你的交易成本
前面几个指标都是「看别人」的流动性。价格影响模型不一样,它回答的是:如果我要买/卖一定数量,会花多少隐性成本?
最常用的形式是线性价格影响模型:
Price_Impact = α + β * Trade_Size + ε
其中Trade_Size是你的交易量占市场成交量的比例。β越大,说明你的交易对价格的冲击越大,流动性越差。
我一般会用分段回归来做这个模型。为什么?因为小单和大单的价格影响不是线性的。小单可能没影响,大单一进去价格就飞了。
import statsmodels.api as sm
def estimate_price_impact(trade_sizes, price_changes):
"""
估计价格影响模型
trade_sizes: 交易量占比序列
price_changes: 对应的价格变动序列
"""
X = sm.add_constant(trade_sizes)
model = sm.OLS(price_changes, X).fit()
return model.params['x1'] # 返回价格影响系数
# 用法
# impact_coef = estimate_price_impact(df['trade_ratio'], df['price_change'])
# print(f'价格影响系数: {impact_coef:.4f}')
实战要点:做LOF套利时,我建议把价格影响模型和你的实际仓位结合起来看。比如你的套利策略每次交易10万元,那就看看10万元对应的价格影响是多少。如果超过预期利润的20%,这个品种就不适合做。
4.5 四个模型怎么选?一张图说清楚
说了这么多,你可能会问:到底用哪个?我的建议是——别只用一个,组合着来。
下面这张图是我自己总结的流动性度量框架,你可以照着这个思路来:
你看这张图,四个模型各有侧重。Amihud和流动性比率适合日常监控,Roll价差和价格影响模型更适合做交易决策。我自己的习惯是:每天开盘前跑一遍四个指标,给所有关注的LOF品种打个分,低于60分的直接剔除出套利候选池。
4.6 实战中的几个坑
最后分享几个我踩过的坑,你遇到了能少走弯路:
- 数据频率不匹配:用日线数据算Amihud,结果和分钟级数据算出来的可能差一个数量级。做LOF套利,至少用分钟级数据。
- 复权问题:分红、送股会破坏价格序列的连续性。Roll价差对这种突变特别敏感,一定要先做复权处理。
- 极端值处理:LOF品种有时候会出现极端行情,比如开盘瞬间价格打到跌停。这些极端值会严重扭曲流动性指标,我一般会做3倍标准差截断。
- 不要迷信单一指标:四个指标都算一遍,如果三个说好一个说差,那大概率是有问题的。我吃过这个亏,以后都是综合判断。
流动性度量这件事,说白了就是给你的交易上个保险。你花10分钟算清楚流动性,可能帮你省下10%的滑点成本。这笔账,怎么算都划算。