跳到正文

Regime Detection: 识别你的因子何时有效

市场在不同的 regime 之间切换——趋势、反转、高波动、低波动。Regime detection 识别当前哪个 regime 活跃,以便你相应调整因子敞口。

第 10 篇 · 共 10 篇 约 2 分钟

一个在趋势明显的牛市表现出色的动量因子,在震荡、反转的市场中可能亏钱。一个在经济扩张期蓬勃发展的价值因子,可能在衰退期被重创。Regime detection 是识别当前市场处于哪个 regime——并相应调整因子敞口的做法。

什么是 Market Regime?

市场 regime 是以特定组合为特征的市场环境:

  • 趋势强度(动量 vs. 均值回归)
  • 波动率水平(高 vs. 低)
  • 相关性结构(多元化有效 vs. 相关性飙升)
  • 流动性条件

Regime 不是永久性的。它们毫无预兆地切换。低波动、趋势的 regime 可以在几天内翻转到高波动、均值回归的 regime(就像 2020 年 3 月那样)。

检测 Regime 的方法

方法 1:隐马尔可夫模型(HMM)

HMM 假设市场在有限数量的未观察状态(“regime”)之间切换。你观察收益;模型推断你处于哪个 regime。

import numpy as np
from hmmlearn import hmm
import pandas as pd

def detect_regimes_hmm(returns, n_states=3):
    """
    使用高斯 HMM 检测市场 regime。

    Parameters
    ----------
    returns : Series 或 DataFrame
        资产收益
    n_states : int
        要检测的 regime 数量

    Returns
    -------
    regimes : Series
        推断的 regime 序列
    model : HMMModel
        拟合的 HMM 模型
    """
    X = returns.values.reshape(-1, 1)

    model = hmm.GaussianHMM(
        n_components=n_states,
        covariance_type="full",
        n_iter=1000
    ).fit(X)

    regimes = model.predict(X)
    regimes_series = pd.Series(regimes, index=returns.index)

    return regimes_series, model


def interpret_regimes(regimes_series, returns):
    """
    解释检测到的 regime 的特征。
    """
    results = {}
    for regime in regimes_series.unique():
        mask = regimes_series == regime
        regime_returns = returns[mask]

        results[regime] = {
            'mean_return': regime_returns.mean(),
            'volatility': regime_returns.std(),
            'n_obs': mask.sum(),
            'pct_of_data': mask.mean()
        }

    return pd.DataFrame(results).T

方法 2:波动率 Regime

相对于历史平均值按波动率水平分类 regime:

def detect_vol_regime(returns, lookback=252, threshold=1.5):
    """
    相对于历史将 regime 分类为低/高波动。
    """
    rolling_vol = returns.rolling(lookback).std()
    avg_vol = rolling_vol.mean()
    current_vol = rolling_vol.iloc[-1]

    if current_vol > threshold * avg_vol:
        return 'high_vol'
    else:
        return 'low_vol'


def compute_rolling_volatility_regimes(returns, window=60, threshold=1.5):
    """
    计算滚动波动率 regime。
    """
    rolling_vol = returns.rolling(window).std()
    avg_vol = rolling_vol.expanding().mean()

    regimes = (rolling_vol > threshold * avg_vol).astype(int)
    return regimes

方法 3:趋势强度 Regime

测量绝对收益动量来分类趋势 vs. 均值回归 regime:

def detect_trend_regime(prices, mom_lookback=60, threshold=0.02):
    """
    检测市场是趋势还是均值回归。
    """
    mom = (prices / prices.shift(mom_lookback) - 1).iloc[-1]

    if mom > threshold:
        return 'trending'
    elif mom < -threshold:
        return 'reversing'
    else:
        return 'choppy'


def dual_momentum_regimes(prices, short_lookback=20, long_lookback=60):
    """
    使用短期和长期动量检测 regime。

    短期和长期都正向 = 趋势确认
    短期和长期都负向 = 逆转确认
    短期和长期矛盾 = 震荡
    """
    short_mom = (prices / prices.shift(short_lookback) - 1).iloc[-1]
    long_mom = (prices / prices.shift(long_lookback) - 1).iloc[-1]

    if short_mom > 0 and long_mom > 0:
        return 'strong_trend'
    elif short_mom < 0 and long_mom < 0:
        return 'mean_reversion'
    elif short_mom > 0 and long_mom < 0:
        return 'short_term_long_reversal'
    elif short_mom < 0 and long_mom > 0:
        return 'short_term_trend'
    else:
        return 'choppy'

使用 Regime 信息

一旦检测到 regime,你可以调整因子敞口:

Regime动量权重均值回归权重低波动权重风险平价
趋势高低低减配
均值回归低高中超配
高波动减配减配高等权
低波动中中中等权
def regime_adjusted_weights(base_weights, current_regime, regime_adjustments):
    """
    根据当前 regime 调整因子权重。
    """
    adjustments = regime_adjustments.get(current_regime, {})
    adjusted = base_weights.copy()

    for factor, adjustment in adjustments.items():
        if factor in adjusted:
            adjusted[factor] *= adjustment

    # 重新归一化
    total = sum(adjusted.values())
    if total > 0:
        adjusted = {k: v / total for k, v in adjusted.items()}

    return adjusted


REGIME_ADJUSTMENTS = {
    'trending': {'momentum': 1.5, 'mean_reversion': 0.5, 'low_vol': 0.7},
    'mean_reversion': {'momentum': 0.5, 'mean_reversion': 1.5, 'low_vol': 1.2},
    'high_vol': {'momentum': 0.7, 'mean_reversion': 0.7, 'low_vol': 1.5},
    'low_vol': {'momentum': 1.0, 'mean_reversion': 1.0, 'low_vol': 1.0},
    'choppy': {'momentum': 0.8, 'mean_reversion': 0.8, 'low_vol': 1.0}
}

HMM Regime 与因子 IC

分析每个 regime 下因子的 IC:

def compute_ic_by_regime(factor_values, returns, regimes):
    """
    计算每个 regime 下的 IC。
    """
    results = {}
    for regime in regimes.unique():
        mask = regimes == regime

        fv = factor_values[mask].dropna()
        ret = returns[mask].dropna()

        common = fv.index.intersection(ret.index)
        if len(common) > 30:
            ic = np.corrcoef(fv[common].values, ret[common].values)[0, 1]
            results[f'regime_{regime}'] = ic
        else:
            results[f'regime_{regime}'] = np.nan

    return pd.Series(results)

常见错误

Regime 估计有噪声。 HMM regime 标签可能频繁切换,即使底层市场没有变化。使用平滑或要求最小 regime 持续时间再切换敞口。

Regime 切换的交易成本。 如果你每周根据噪声 regime 估计切换因子, transaction costs 会摧毁收益。只有当 regime 信号强烈且持久时才切换。

只用一个指标。 单一波动率或动量指标可能对某些市场无效。组合多个指标(波动率 + 趋势 + 相关性)给出更稳健的 regime 估计。

Regime 预测是困难的。 检测当前 regime 相对简单。预测 regime 切换时间要困难得多。大多数 regime 切换策略由于过早或过晚切换而失败。

在知识体系中的位置

Regime detection 与因子衰减和因子组合密切相关。衰减在市场结构变化期间加速。因子组合应根据当前 regime 加权。横截面稳定性测试在不同 regime 下因子是否一致有效。

参考文献

  • Ang, Asset Management, 第 10 章. Oxford University Press, 2014. ISBN: 978-0-19-995932-3.
  • López de Prado, Advances in Financial Machine Learning, 第 10 章. Wiley, 2018. ISBN: 978-1-119-48208-6.
  • Hamilton, Time Series Analysis. Princeton University Press, 1994. ISBN: 978-0-691-04289-3.