1. 板块轮动追高风险的识别与量化框架搭建

为什么追高风险需要“框架”而不是“感觉”

板块轮动中的追高风险,本质是“短期涨幅已经透支了轮动动能”的状态。单看涨幅会误判:强势板块可能继续加速,弱势板块也可能只是超跌反弹。因此需要一个可复用的量化框架,把风险拆成三个可观测维度,再合成一个每日可比的评分。

本框架定义三类指标,全部基于板块指数日线数据(收盘价、成交量)与大盘基准指数计算:

  • 轮动强度 R:板块相对大盘的超额收益在近 N 日横截面排名中的变化速度,刻画“资金是否正在加速涌入”。
  • 涨幅偏离 D:板块收盘价相对其 20 日均线的偏离度,刻画“短期价格是否已远离中枢”。
  • 量能异动 V:当日成交量相对其 20 日均量的放大倍数,刻画“是否出现情绪化放量”。

三者分别做横截面标准化(Z-score),加权合成追高风险评分 RiskScore,取值范围经压缩后落在 0~100,便于跨日、跨板块比较。

框架结构与数据流

下图展示从原始日线数据到每日风险评分的完整链路:三条指标支路并行计算,各自标准化后进入加权合成,最终输出板块×日期的评分矩阵,并给出风险分档。

板块指数日线 收盘价 / 成交量 约 30 个行业板块 大盘基准指数 沪深300 日线收盘 轮动强度 R 超额收益 = 板块收益 − 大盘收益 近5日横截面排名变化 Δrank R = Δrank 的标准化值 涨幅偏离 D MA20 = 近20日收盘均值 D = (收盘 − MA20) / MA20 正值越大,越远离中枢 量能异动 V VOL_MA20 = 近20日均量 V = 当日成交量 / VOL_MA20 V > 1.5 视为放量异动 横截面标准化 每个交易日 对全部板块做 Z = (x − μ) / σ 消除量纲差异 保证跨日可比 加权合成 RiskScore = 0.4·Z_R + 0.4·Z_D + 0.2·Z_V 压缩到 0~100 每日每板块一个值 输出:板块 × 日期 风险评分矩阵 低风险 < 40 | 关注 40~60 | 高风险 60~80 | 极高 ≥ 80 用于每日盘后扫描,标记需警惕追高的板块 评分仅作风险提示,不构成买卖信号 Z_R Z_D Z_V

指标定义与计算口径

为避免“看起来合理但算不出来”,先把每个指标的口径固定下来。以下以单个板块 s、交易日 t 为例,close 为收盘价,vol 为成交量。

指标计算式方向含义典型阈值
轮动强度 R超额收益 ex = ret_s − ret_bench;对 ex 做 5 日累计后取横截面排名,R = rank_t − rank_{t−5}R 越大,排名上升越快,资金加速流入R ≥ 3 视为轮动加速
涨幅偏离 DD = (close_t − MA20_t) / MA20_tD 越大,价格越偏离 20 日中枢D ≥ 0.08 视为短期偏离偏高
量能异动 VV = vol_t / VOL_MA20_tV 越大,放量越明显V ≥ 1.5 视为放量异动

三个指标都做了“越大越危险”的方向统一,因此合成时直接加权即可,无需再翻转符号。

可复用的计算实现

下面是一段自洽的 Python 实现,输入为宽表格式的板块收盘价与成交量(行=日期,列=板块),以及大盘收盘价序列。函数返回每日每板块的 RiskScore。

import numpy as np
import pandas as pd

def build_chase_risk(close_wide, vol_wide, bench_close,
                     w=(0.4, 0.4, 0.2), ma_win=20, rank_win=5):
    """
    close_wide : DataFrame, index=日期, columns=板块, 收盘价
    vol_wide   : DataFrame, 同形状, 成交量
    bench_close: Series, index=日期, 大盘收盘价
    w          : (w_R, w_D, w_V) 权重
    返回: DataFrame, 同形状, 追高风险评分 0~100
    """
    # 1) 轮动强度 R:超额收益的横截面排名变化
    ret_s = close_wide.pct_change()
    ret_b = bench_close.pct_change().reindex(close_wide.index)
    ex = ret_s.sub(ret_b, axis=0)                 # 每日超额收益
    ex_cum = ex.rolling(rank_win).sum()           # 5日累计超额
    rank = ex_cum.rank(axis=1, ascending=False)   # 1=最强
    R = rank.shift(rank_win) - rank               # 排名上升为正

    # 2) 涨幅偏离 D
    ma20 = close_wide.rolling(ma_win).mean()
    D = (close_wide - ma20) / ma20

    # 3) 量能异动 V
    vma20 = vol_wide.rolling(ma_win).mean()
    V = vol_wide / vma20

    # 4) 横截面 Z-score 标准化
    def zscore(df):
        mu = df.mean(axis=1)
        sd = df.std(axis=1).replace(0, np.nan)
        return df.sub(mu, axis=0).div(sd, axis=0)

    ZR, ZD, ZV = zscore(R), zscore(D), zscore(V)

    # 5) 加权合成并压缩到 0~100
    raw = w[0]*ZR + w[1]*ZD + w[2]*ZV
    score = 50 + 15 * raw          # 均值50, 1个标准差约15分
    return score.clip(0, 100)

关键点说明:rank.shift(rank_win) - rank 中,排名数值变小代表变强,所以“旧排名 − 新排名”为正表示排名上升;zscore 按行(每个交易日)做横截面标准化,保证不同日期的评分可比;50 + 15*raw 是线性映射,使评分集中在 0~100 且便于分档。

用示例数据验证框架输出

构造 4 个板块、60 个交易日的示例数据,其中“板块A”在第 40 日后出现连续放量拉升,用于检验评分是否能识别追高风险。

np.random.seed(7)
dates = pd.bdate_range("2024-01-01", periods=60)
names = ["板块A", "板块B", "板块C", "板块D"]

# 基础随机游走
base = pd.DataFrame(
    np.cumprod(1 + np.random.normal(0.0005, 0.012, (60, 4)), axis=0),
    index=dates, columns=names) * 1000

# 板块A 后20日加速上涨并放量
base["板块A"].iloc[40:] *= np.linspace(1.0, 1.18, 20)
vol = pd.DataFrame(
    np.random.normal(1.0, 0.15, (60, 4)).clip(0.4),
    index=dates, columns=names) * 1e6
vol["板块A"].iloc[40:] *= 1.9

bench = pd.Series(
    np.cumprod(1 + np.random.normal(0.0003, 0.008, 60)), index=dates) * 3000

score = build_chase_risk(base, vol, bench)
print(score.tail(3).round(1))

预期现象(示例数据下的合理结果,非实测):板块A 在加速段后期,其 RiskScore 应显著高于其他板块,通常进入 70 以上区间;而板块B/C/D 因无放量加速,评分多在 40~60 附近波动。若板块A 评分未抬升,应检查 rank_win 是否过长导致排名变化被平滑,或 vol 单位是否与均量一致。

评分是相对量:同一天内比较板块之间的风险高低最有效;跨日期比较时,因横截面标准化,绝对数值会随当日板块分化程度变化,建议结合分位数而非固定阈值使用。

评分分档与使用边界

  • 0~40 低风险:轮动、偏离、量能均未共振,通常不构成追高风险。
  • 40~60 关注:单项指标偏高,需结合板块基本面与市场环境判断。
  • 60~80 高风险:多项指标共振,短期追入的回撤概率上升。
  • 80~100 极高风险:典型“加速末端”特征,应重点警惕。

框架输出的是风险提示,不是卖出或做空信号;它的价值在于把“这个板块是不是涨得太急了”转化为每日可扫描、可回溯、可调参的数值,为后续的阈值设定与信号验证提供统一底座。