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 综合拥挤度指标

三个指标单独看,都有噪音。我习惯把它们合成一个综合指标。

具体做法是这样的:

  1. 对每个指标,计算它在过去120天内的分位数
  2. 取三个分位数的平均值,得到综合拥挤度
  3. 综合拥挤度超过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 知识体系:拥挤度判断的核心逻辑

下面这张图,是我自己梳理的拥挤度判断流程。每次做因子择时,我都会过一遍这个逻辑。

因子拥挤度判断流程 市场数据输入 换手率比值 波动率比值 资金流比率 分位数计算 分位数计算 分位数计算(反向) 综合拥挤度指标 拥挤度 > 0.8 ?

4.5 实战中的避坑指南

指标建好了,但实战中还是有很多坑。我踩过的,跟大家说说。

避坑1:不要只看单一指标

我曾经只盯着换手率看,结果换手率没到阈值,但资金流早就跑光了。后来我才明白,三个指标要结合起来看。任何一个指标单独触发,都不足以做出决策。至少要两个指标同时触发,我才会行动。

避坑2:注意指标的滞后性

换手率和波动率都是滞后指标。它们反映的是已经发生的事。资金流相对领先一些,但也不完美。我的做法是:用资金流做预警,用换手率和波动率做确认。资金流先变差,我就开始减仓。等换手率和波动率也变差,我就清仓。

避坑3:不同因子的阈值不一样

价值因子和动量因子的拥挤阈值完全不同。价值因子换手率低,波动率也低。动量因子正好相反。所以不要用一套参数打天下。我一般会为每个因子单独回测,找到它自己的阈值。

4.6 小结

市场情绪指标,说白了就是帮我们判断「这趟车还能不能上」。换手率看交易热度,波动率看分歧程度,资金流看聪明钱的方向。三个指标合在一起,就能比较准确地判断因子是否处于拥挤状态。

嗯,这一章的内容就到这里。记住,拥挤不是末日,但忽视拥挤一定是末日。

专注资料整理