4、市场情绪指标:构建市场情绪指标(换手率、波动率、资金流),用于判断因子是否处于拥挤状态。
做因子投资最怕什么?
不是因子失效,而是因子被挤爆。
我见过太多人,辛辛苦苦挖出一个好因子,回测曲线漂亮得不行。结果一上实盘,收益直接塌方。为什么?因为大家都发现了这个因子,一窝蜂冲进去,把alpha给挤没了。
所以这一章,我们来聊聊怎么用市场情绪指标,提前嗅到拥挤的味道。
4.1 拥挤的本质:人多的地方不要去
因子拥挤,说白了就是太多资金在同一个方向上下注。你想想看,当所有人都买小盘股,小盘股就不便宜了。当所有人都做多波动率,波动率策略就不赚钱了。
我个人习惯把因子拥挤分成两个阶段:
- 拥挤形成期:资金持续流入,因子收益还在涨,但波动开始加大
- 拥挤崩溃期:资金突然流出,因子收益暴跌,踩踏发生
我们要做的,就是在拥挤形成期的后半段,开始减仓。别等到崩溃了再跑,那时候门都堵死了。
核心观点:拥挤不是非黑即白的。它是一个连续的状态。我们需要用指标来量化这个状态。
4.2 三个核心情绪指标
我常用的市场情绪指标有三个:换手率、波动率、资金流。它们从不同角度刻画了市场的拥挤程度。
4.2.1 换手率:交易的温度计
换手率高,说明交易活跃。但高到离谱,就说明有人在频繁倒手,可能是量化资金在反复收割。
我记得有一次,我跟踪的一个价值因子,换手率突然飙升到历史95分位数以上。我当时就觉得不对劲。果然,两周后因子收益开始大幅回撤。
具体怎么算?我一般用过去20天的平均换手率,再除以过去120天的平均换手率,得到一个比值。这个比值超过1.5,我就开始警惕了。
import pandas as pd
import numpy as np
def calc_turnover_ratio(turnover_series, short_window=20, long_window=120):
"""
计算换手率比值
short_window: 短期窗口,默认20天
long_window: 长期窗口,默认120天
"""
short_avg = turnover_series.rolling(window=short_window).mean()
long_avg = turnover_series.rolling(window=long_window).mean()
ratio = short_avg / long_avg
return ratio
# 示例用法
# turnover_data = pd.Series(...)
# ratio = calc_turnover_ratio(turnover_data)
# 当 ratio > 1.5 时,发出拥挤预警
4.2.2 波动率:市场的情绪放大器
波动率上升,通常意味着分歧加大。有人拼命买,有人拼命卖。这种时候,因子很容易出现剧烈波动。
我曾经犯过一个错误:看到一个动量因子波动率很低,觉得它很稳,就重仓了。结果波动率突然爆发,三天亏掉了两个月的收益。后来我学乖了,波动率一抬头,我就减仓。
我常用的波动率指标是已实现波动率,用过去20天的日收益率标准差来算。然后同样做一个比值,看短期波动率是否显著高于长期波动率。
def calc_volatility_ratio(returns_series, short_window=20, long_window=60):
"""
计算波动率比值
"""
short_vol = returns_series.rolling(window=short_window).std() * np.sqrt(252)
long_vol = returns_series.rolling(window=long_window).std() * np.sqrt(252)
ratio = short_vol / long_vol
return ratio
# 当 ratio > 1.3 时,波动率异常放大,需警惕
4.2.3 资金流:聪明钱的方向
资金流是最直接的指标。大单资金持续流入,说明机构在加仓。大单资金持续流出,说明机构在跑路。
我个人习惯用主力资金净流入率。如果这个指标连续5天为负,同时因子还在涨,那就是典型的「量价背离」。这时候千万别追,很可能是散户在接盘。
def calc_money_flow_ratio(large_buy_volume, large_sell_volume):
"""
计算主力资金净流入率
"""
net_inflow = large_buy_volume - large_sell_volume
total_volume = large_buy_volume + large_sell_volume
ratio = net_inflow / total_volume
return ratio
# ratio > 0.2 表示资金大幅流入
# ratio < -0.2 表示资金大幅流出
4.3 综合拥挤度指标
三个指标单独看,都有噪音。我习惯把它们合成一个综合指标。
具体做法是这样的:
- 对每个指标,计算它在过去120天内的分位数
- 取三个分位数的平均值,得到综合拥挤度
- 综合拥挤度超过0.8,我就认为因子处于拥挤状态
def calc_crowding_index(turnover_ratio, vol_ratio, money_flow_ratio, window=120):
"""
计算综合拥挤度指标
"""
# 计算分位数
turn_percentile = turnover_ratio.rank(pct=True)
vol_percentile = vol_ratio.rank(pct=True)
money_percentile = money_flow_ratio.rank(pct=True)
# 注意:资金流是反向指标,流出越多越拥挤
# 所以用 1 - 分位数
money_percentile = 1 - money_percentile
# 综合拥挤度
crowding_index = (turn_percentile + vol_percentile + money_percentile) / 3
return crowding_index
# crowding_index > 0.8 时,触发拥挤预警
小技巧:资金流指标的方向要特别注意。资金流出意味着拥挤加剧,所以要用1减去它的分位数。这个细节我一开始也搞反过,后来回测才发现不对劲。
4.4 知识体系:拥挤度判断的核心逻辑
下面这张图,是我自己梳理的拥挤度判断流程。每次做因子择时,我都会过一遍这个逻辑。
4.5 实战中的避坑指南
指标建好了,但实战中还是有很多坑。我踩过的,跟大家说说。
避坑1:不要只看单一指标
我曾经只盯着换手率看,结果换手率没到阈值,但资金流早就跑光了。后来我才明白,三个指标要结合起来看。任何一个指标单独触发,都不足以做出决策。至少要两个指标同时触发,我才会行动。
避坑2:注意指标的滞后性
换手率和波动率都是滞后指标。它们反映的是已经发生的事。资金流相对领先一些,但也不完美。我的做法是:用资金流做预警,用换手率和波动率做确认。资金流先变差,我就开始减仓。等换手率和波动率也变差,我就清仓。
避坑3:不同因子的阈值不一样
价值因子和动量因子的拥挤阈值完全不同。价值因子换手率低,波动率也低。动量因子正好相反。所以不要用一套参数打天下。我一般会为每个因子单独回测,找到它自己的阈值。
4.6 小结
市场情绪指标,说白了就是帮我们判断「这趟车还能不能上」。换手率看交易热度,波动率看分歧程度,资金流看聪明钱的方向。三个指标合在一起,就能比较准确地判断因子是否处于拥挤状态。
嗯,这一章的内容就到这里。记住,拥挤不是末日,但忽视拥挤一定是末日。