1. 流动性定义与核心指标
大家好,我是老张。今天咱们聊聊流动性——这个在量化交易里绕不开的话题。
流动性是什么?说白了,就是你能不能用合理的价格,快速地把资产变成现金。你想想看,如果你手里拿着一张股票,想卖的时候没人接盘,或者得打个八折才能出手,那这资产就“不流动”。
我刚开始做量化那会儿,吃过一次亏。策略回测跑得漂亮,年化收益30%+,结果实盘一跑,滑点直接吃掉一半利润。后来复盘才发现,我选的标的流动性太差了。嗯,从那以后,流动性指标就成了我策略里的“标配”。
核心观点:流动性差的资产,再好的策略也是纸上谈兵。流动性风险是量化策略的“隐形杀手”。
1.1 为什么流动性对量化策略重要?
这个问题我问过不少刚入行的朋友。他们的第一反应往往是:“流动性好,交易成本低呗。”对,但不全对。
流动性影响的不只是成本,还有策略的容量、执行效率,甚至信号的有效性。举个例子:
- 交易成本:买卖价差越大,你进出的摩擦成本越高。高频策略尤其敏感。
- 策略容量:流动性不足,大资金一进去就把价格打飞了。我见过有人用1000万做小盘股,结果自己把自己拉涨停。
- 信号衰减:你想买的量,市场接不住,信号还没执行完,价格已经变了。
- 风险控制:遇到黑天鹅,流动性枯竭,你想止损都出不来。2015年股灾就是活生生的例子。
所以,流动性不是锦上添花,而是策略的“地基”。地基不稳,楼盖得再高也得塌。
1.2 核心指标详解
接下来,我带你逐个拆解几个最常用的流动性指标。每个指标我都会给出数学定义和Python实现,方便你直接拿来用。
1.2.1 买卖价差(Bid-Ask Spread)
这是最直观的流动性指标。买一价和卖一价的差值,就是你交易一单位需要付出的“过路费”。
数学定义:
绝对价差 = 卖一价 - 买一价
相对价差 = (卖一价 - 买一价) / 中间价
中间价 = (买一价 + 卖一价) / 2
Python计算:
import pandas as pd
def calc_spread(df):
"""
df 必须包含 ask_price1, bid_price1 两列
"""
df['mid_price'] = (df['ask_price1'] + df['bid_price1']) / 2
df['abs_spread'] = df['ask_price1'] - df['bid_price1']
df['rel_spread'] = df['abs_spread'] / df['mid_price']
return df
# 示例
data = pd.DataFrame({
'ask_price1': [10.05, 10.10, 10.08],
'bid_price1': [10.00, 10.02, 10.03]
})
result = calc_spread(data)
print(result[['abs_spread', 'rel_spread']])
个人经验:我习惯用相对价差,因为不同价格的股票绝对值没法直接比。比如茅台和工商银行,绝对价差差好几倍,但相对价差都在0.1%左右。
1.2.2 市场深度(Market Depth)
价差只告诉你第一档的成本,但如果你想买100万股呢?这时候就得看市场深度了。
定义:在某个价格水平上,市场能承受的订单数量。通常看买卖各档位的累计挂单量。
Python计算:
def calc_depth(df, levels=5):
"""
计算买卖各档位的累计深度
df 需包含 ask_vol1~ask_vol5, bid_vol1~bid_vol5
"""
ask_cols = [f'ask_vol{i}' for i in range(1, levels+1)]
bid_cols = [f'bid_vol{i}' for i in range(1, levels+1)]
df['ask_depth'] = df[ask_cols].sum(axis=1)
df['bid_depth'] = df[bid_cols].sum(axis=1)
df['total_depth'] = df['ask_depth'] + df['bid_depth']
return df
注意:市场深度是动态变化的。我曾经在回测里用了静态深度数据,结果实盘时发现深度只有回测的一半。后来我改用实时快照数据,才解决了这个问题。
1.2.3 成交量(Volume)
成交量是最常用的流动性代理指标。量越大,通常流动性越好。但要注意,成交量是事后指标,不能预测未来的流动性。
Python计算:
# 计算日成交量滚动均值
df['volume_ma5'] = df['volume'].rolling(window=5).mean()
df['volume_std'] = df['volume'].rolling(window=20).std()
# 成交量异常检测
df['volume_zscore'] = (df['volume'] - df['volume'].rolling(20).mean()) / df['volume'].rolling(20).std()
1.2.4 换手率(Turnover Rate)
换手率 = 成交量 / 流通股本。它剔除了股本规模的影响,更适合跨股票比较。
数学定义:
换手率 = 当日成交量 / 流通股本
Python计算:
def calc_turnover(df, shares_outstanding):
"""
shares_outstanding: 流通股本(股数)
"""
df['turnover'] = df['volume'] / shares_outstanding
return df
避坑指南:我曾经用总股本代替流通股本算换手率,结果发现指标严重偏低。后来才意识到,很多大股东的股份是不流通的。一定要用流通股本!
1.2.5 Amihud非流动性指标
这是学术界用得最多的流动性指标之一。它衡量的是“每单位成交量引起的价格变化”。价格变化越大,说明流动性越差。
数学定义:
Amihud_i = (1/N) * Σ(|r_i| / volume_i)
其中 r_i 是第 i 分钟的收益率,volume_i 是第 i 分钟的成交量(金额)。
Python计算:
def calc_amihud(df):
"""
df 需包含 close, volume(金额), 按时间排序
"""
df['return'] = df['close'].pct_change().abs()
df['amihud_raw'] = df['return'] / df['volume']
# 通常取滚动均值
df['amihud'] = df['amihud_raw'].rolling(window=20).mean()
# 处理无穷大
df['amihud'] = df['amihud'].replace([float('inf'), -float('inf')], float('nan'))
return df
重要提示:Amihud指标的值通常非常小(10^-6量级),建议乘以10^6或取对数后使用。我一般取log(1 + Amihud * 10^6)做标准化。
1.3 知识体系总览
下面这张图,我把本章的核心逻辑梳理了一下。你可以把它当作一个“流动性指标工具箱”。
1.4 指标对比与选择建议
这么多指标,到底用哪个?我的建议是:不要只用单一指标。每个指标都有它的盲区。
| 指标 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 买卖价差 | 直观、实时性强 | 只反映第一档,大单不适用 | 高频策略、小单交易 |
| 市场深度 | 反映多档位承接能力 | 数据量大,更新频繁 | 大单执行、算法交易 |
| 成交量 | 数据易得,稳定 | 事后指标,有滞后 | 中低频策略、选股 |
| 换手率 | 跨股票可比 | 依赖流通股本数据 | 选股、行业对比 |
| Amihud | 综合价格与成交量 | 计算复杂,值域不稳定 | 学术研究、因子构建 |
我的组合拳:做选股策略时,我一般用换手率+Amihud;做执行策略时,用价差+深度。不同场景,不同工具。
1.5 小结
流动性不是玄学,它是可以量化、可以计算的。今天讲的五个指标,每个都有它的数学定义和Python实现。你拿回去就能用。
记住一句话:流动性是策略的“安全带”。系好安全带,才能开快车。
下一章,我会讲如何用这些指标构建一个流动性评分系统。到时候咱们再聊。
公众号:蓝海资料掘金营,微信deep3321