Skip to content

一个被低估的指标,专治K线图里的假动作(附完整代码)

作者:老余捞鱼

原创不易,转载请标明出处及原作者。

文中示例仅用于技术讨论,不构成任何操作建议。
量化策略开发应以学习和技术交流为目的。
本号不荐股、不卖课、不承诺收益。
市场有风险,请合法合规投资。

本文约 3200 字 | 预计阅读 8-10 分钟

· · ·

每个做过量化的人大概都经历过这个:策略回测年化 30%+,实盘第一个月就回撤 8%。问题往往不是策略本身错了,而是你在震荡市跑趋势策略,在趋势市跑均值回归。

说白了,你选对了武器,但打错了战场。

今天聊的这个指标叫 Choppiness Index(CHOP),中文可以叫”震荡指数”或”碎片指数”。它不告诉你涨还是跌,只回答一个问题:

市场现在到底在不在动?

一、CHOP 是什么?

CHOP 由澳大利亚大宗商品交易员 E.W. Dreiss 在 1990 年代开发。核心思路其实很直觉:

一个市场如果走了很远的路但还是一条直线,那它在趋势中。如果走了很多路但最后回到起点附近,那它在震荡。

就这么简单。它不判断方向,只检测”市场性格”。

CHOP 的值在 0 到 100 之间波动,规则也直接:

CHOP 值市场状态含义
< 38.2趋势市方向明确,低熵
> 61.8震荡市来回拉锯,高熵
38.2 – 61.8中性过渡期,方向不明

注意这两个阈值:38.2 和 61.8。不是拍脑袋定的,来自 Fibonacci 黄金比例。Dreiss 选这两个值是因为金融市场在黄金比例附近长期表现出自相似的分形特征。

二、公式拆解

CHOP 的公式乍看有点复杂,但逻辑很漂亮:

CHOP = 100 × log10(ATR_Sum(n) / (Highest_High(n) − Lowest_Low(n))) / log10(n)

拆成五步来看。

图1:CHOP公式的核心逻辑——总路程除以直线距离

第一步:True Range(真实波幅)

每根K线的真实波幅取三个值中的最大值:

TR = max(
  High − Low,
  |High − Previous Close|,
  |Low − Previous Close|
)

你可能会问:为什么不用简单的 High 减 Low?那是因为真实波幅要捕捉隔夜跳空。开盘跳空也是市场在动,不能假装没看见。

第二步:ATR 总和

把窗口期(通常 14 根K线)内所有 TR 加起来:

ATR_Sum = Σ TR over n periods

这个值告诉你:在窗口期内,价格总共走了多少路。注意是”总路程”,不是”位移”。

第三步:价格区间

窗口期内的最高点减最低点:

Range = Highest_High(n) − Lowest_Low(n)

这是直线距离,从最低点到最高点。

第四步:比值

关键来了。ATR 总和除以区间:

Ratio = ATR_Sum / Range
  • 比值接近 1.0:价格几乎笔直走了一个方向,趋势明确
  • 比值远大于 1.0:价格来回折腾,走了很多冤枉路,震荡

打个比方:你从北京去上海。直线距离 1000 公里。如果你京沪高铁直达,路程就是 1000 公里出头,比值接近 1。如果你先飞武汉再坐动车到上海,走了 3000 公里,比值就是 3。CHOP 干的就是这个事:衡量价格”走冤枉路”的程度。

第五步:对数归一化

用 log10 把结果压缩到 0-100 之间,不管你用什么周期、什么品种,结果都在同一个尺度上可比。

三、状态检测:CHOP 真正的价值

CHOP 不是一个静态指标。它最有用的功能是市场状态转换检测

市场不是随机在趋势和震荡之间切换的,它有循环:

震荡 → [突破] → 趋势 → [衰竭] → 震荡

图2:市场状态循环——震荡、突破、趋势、衰竭

当一段长时间的震荡结束(CHOP 从 61.8 以上往 38.2 掉),往往意味着突破在即。多空双方打到筋疲力尽,一旦某一方占上风,行情可能很猛。

反过来,CHOP 从低位快速拉升,说明趋势可能在走下坡路。

这个信息比”价格在涨还是在跌”值钱得多。因为同样是涨,在趋势市里涨和在震荡市里涨,完全是两回事。

四、代码实操

这里我把核心部分拎出来讲。

4.1 数据清洗

def clean_financial_data(df):
    if isinstance(df.columns, pd.MultiIndex):
        df.columns = df.columns.get_level_values(0)
    df.index = pd.to_datetime(df.index)
    df = df.sort_index()
    df = df[~df.index.duplicated(keep='first')]
    required_cols = ['Open', 'High', 'Low', 'Close', 'Volume']
    df = df[required_cols]
    df = df.apply(pd.to_numeric, errors='coerce')
    df = df.ffill().bfill()
    df = df[(df[['Open', 'High', 'Low', 'Close']] > 0).all(axis=1)]
    df = df[df['Volume'] > 0]
    return df

这步看着无聊,其实很关键。API 拉来的数据永远是脏的:MultiIndex 列名、重复时间戳、缺失值、停牌导致的零价格。垃圾进,垃圾出。清洗数据是任何可靠信号的基础。

4.2 CHOP 计算

def calculate_choppiness(df, window=14):
    high_low = df['High'] - df['Low']
    high_close = np.abs(df['High'] - df['Close'].shift(1))
    low_close = np.abs(df['Low'] - df['Close'].shift(1))
    tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
    atr_sum = tr.rolling(window=window).sum()
    highest_high = df['High'].rolling(window=window).max()
    lowest_low = df['Low'].rolling(window=window).min()
    denominator = (highest_high - lowest_low).replace(0, np.nan)
    chop = 100 * np.log10(atr_sum / denominator) / np.log10(window)
    return chop

三个实现细节要注意:

  • .shift(1) 取前一根K线的收盘价,计算 True Range 必须用前收,不能当前收盘,否则有未来函数。
  • .replace(0, np.nan)  防止除零:停牌或周末跳空可能导致窗口内最高价等于最低价。
  • 默认 window=14 是Dreiss原始设定,也有人用20或21做更平滑的信号。

4.3 状态标注

脚本拉了 10 只美股(AAPL、MSFT、NVDA、TSLA 等)2024 年以来的日线数据,标注市场状态:

========================================
 LATEST MARKET REGIMES
========================================
Ticker  CHOP    Regime
AAPL    35.43   TRENDING
MSFT    58.46   NEUTRAL
GOOGL   32.33   TRENDING
AMZN    57.95   NEUTRAL
NVDA    46.70   NEUTRAL
TSLA    37.23   TRENDING
META    40.20   NEUTRAL
NFLX    44.82   NEUTRAL
AMD     36.10   TRENDING
COIN    54.28   NEUTRAL
========================================

一眼就能看出谁在走趋势,谁在震荡。这个表做策略筛选的时候特别好用。

五、实战四条

我根据自己的使用经验,整理成四条实操建议。

图3:CHOP做策略过滤器——只在趋势市放行

  • 第一条:CHOP 做过滤器,不做信号别拿 CHOP 的交叉单独做交易信号。它的正确用法是当”门卫”:趋势策略只在 CHOP < 38.2 时允许开仓,均值回归策略只在 CHOP > 61.8 时进场。中间区域?观望。
  • 第二条:变化速率比绝对值重要CHOP 从 55 连续往下掉了 5 天,比一个静态的 38 更有意义。指标的变化速度本身就是信号。我自己的做法是给 CHOP 算一个 5 日 ROC(Rate of Change),下降到某个阈值就提示”趋势可能要来了”。
  • 第三条:配合成交量从震荡切换到趋势,如果有成交量放大配合,可靠性高很多。成交量是燃料,CHOP 告诉你引擎在转,成交量告诉你油够不够。
  • 第四条:多周期对齐日线CHOP反映的是周线级别的市场性格。如果你在日线和周线上同时看到 CHOP<38.2,趋势持续性会强很多,多周期对齐是过滤假信号的有效手段。

六、完整代码

完整源代码 Google Colab 地址如下:

https://colab.research.google.com/drive/1t33-oG2iVr_5o1wB1aiHl2llT5UP3pu5

脚本流程:拉数据 → 清洗 → 算 CHOP → 标注状态 → Plotly 画图(价格图 + CHOP 副图,背景按状态着色)→ 多股票循环 → 汇总表。

拿去改改就能跑自己的标的池。

七、观点总结

CHOP 指标最让我喜欢的一点是:它不装。很多指标试图告诉你”会涨还是会跌”,结果经常打脸。CHOP 只说一句”市场现在在不在动”,反而更可靠。

市场大概有 70% 的时间处于中性或震荡状态。大部分散户就是在这个阶段亏钱的,把噪音当信号。CHOP 给你一个量化工具:噪音就别碰,等市场真走起来再说。

风险提示:本文仅供参考,不构成投资建议。投资有风险,入需谨慎。
版权声明:本文为原创内容,转载请注明出处。


#ChoppinessIndex #量化策略 #市场状态检测 #趋势过滤 #技术指标 #Python量化 #ATR #Fibonacci #交易系统 #程序化交易

Published inAI&Invest专栏

Be First to Comment

    发表回复