2. 震荡行情识别

做POV策略这些年,我最大的体会就是:趋势行情靠胆量,震荡行情靠手艺。很多朋友在震荡市里亏钱,不是策略不对,而是根本不知道自己正处在震荡中。今天我们就来聊聊,怎么用量化手段把震荡行情「抓」出来。

2.1 震荡行情的定义

先给个直观的定义:震荡行情,就是价格在一个区间内来回波动,没有明确的方向。说白了,多空双方在打架,谁也没打赢。

从量化角度看,我习惯用三个条件来判定:

  • 价格波动幅度有限:最高价和最低价的差值,在某个百分比以内
  • 无明显趋势:均线走平,或者短期均线围绕长期均线上下穿梭
  • 成交量相对萎缩:没有放量突破的信号

嗯,这里要注意:震荡不等于「没行情」。恰恰相反,震荡行情里波动率虽然不大,但节奏感很强,非常适合POV这类做市策略。

核心观点:震荡行情是POV策略的「舒适区」。趋势行情里POV容易吃瘪,但震荡市里,POV的订单簿管理能力能发挥到极致。

2.2 震荡行情的量化指标

光靠肉眼判断可不行。我一般用两个指标来量化震荡——ATR和布林带宽度。这两个指标各有侧重,配合使用效果更好。

2.2.1 ATR(平均真实波幅)

ATR衡量的是价格波动的剧烈程度。震荡行情里,ATR通常处于低位。我个人习惯用20日ATR,然后看它的百分位。

import pandas as pd
import numpy as np

def calculate_atr(df, period=20):
    high = df['high']
    low = df['low']
    close = df['close'].shift(1)
    
    tr1 = high - low
    tr2 = abs(high - close)
    tr3 = abs(low - close)
    
    tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
    atr = tr.rolling(window=period).mean()
    
    return atr

# 判断震荡:ATR处于近60天的最低20%分位
def is_range_market(atr_series, lookback=60, threshold=0.2):
    current_atr = atr_series.iloc[-1]
    hist_atr = atr_series.iloc[-lookback:]
    percentile = (hist_atr < current_atr).mean()
    return percentile < threshold

我在项目中遇到过一个问题:ATR在横盘初期会快速下降,但到了横盘末期反而会微微抬头。这是因为市场在积蓄力量。所以单纯看ATR绝对值不够,要看它的相对位置。

小技巧:ATR配合价格位置一起看。如果ATR低且价格在布林带中轨附近,震荡概率超过70%。

2.2.2 布林带宽度

布林带宽度 = (上轨 - 下轨) / 中轨。这个指标直接反映了价格区间的宽窄。震荡行情里,布林带会「收窄」,像蛇一样蜷缩起来。

def calculate_bollinger_width(df, period=20, std_dev=2):
    close = df['close']
    sma = close.rolling(window=period).mean()
    std = close.rolling(window=period).std()
    
    upper = sma + std_dev * std
    lower = sma - std_dev * std
    
    width = (upper - lower) / sma
    return width

# 布林带宽度处于历史低位,说明震荡
def is_bollinger_squeeze(width_series, lookback=60, threshold=0.2):
    current_width = width_series.iloc[-1]
    hist_width = width_series.iloc[-lookback:]
    percentile = (hist_width < current_width).mean()
    return percentile < threshold

你想想看,布林带宽度和ATR其实是一回事——都在度量波动率。但布林带宽度有个好处:它天然做了归一化,不同品种之间可以直接比较。

指标 震荡特征 阈值参考 注意事项
ATR 处于近60天最低20%分位 20%分位 注意ATR在横盘末期会提前上升
布林带宽度 宽度收窄至历史低位 20%分位 不同品种阈值不同,需动态调整
两者结合 同时满足条件 —— 信号更可靠,但会滞后

2.3 震荡行情的分类

震荡也分三六九等。我曾经因为没区分震荡类型,在POV策略里吃了大亏。后来我总结出三种常见的震荡形态:

2.3.1 水平震荡(矩形整理)

价格在两条平行线之间来回跑。这是最「老实」的震荡,上下边界清晰。POV策略在这种行情里表现最好——你可以在上边界附近挂卖单,下边界附近挂买单。

2.3.2 收敛震荡(三角形整理)

价格波动幅度越来越小,像漏斗一样。这种震荡最危险——因为它往往预示着突破。我记得有一次,布林带宽度已经缩到极致,我还在用POV策略做市,结果一根大阳线直接把我打穿。

避坑指南:收敛震荡里,POV策略的库存管理要格外小心。我曾经在收敛三角形末端持仓过重,突破时直接亏掉一周的利润。建议在收敛震荡中降低仓位,或者干脆暂停POV。

2.3.3 扩张震荡(喇叭形整理)

价格波动幅度越来越大。这种震荡比较少见,但一旦出现,POV策略反而有机会——因为波动率在放大,做市商可以赚到更宽的价差。

怎么区分这三种?我一般用线性回归的斜率来判断:

def classify_range(high, low, period=20):
    # 计算上下轨的斜率
    high_slope = np.polyfit(range(period), high[-period:], 1)[0]
    low_slope = np.polyfit(range(period), low[-period:], 1)[0]
    
    if abs(high_slope) < 0.001 and abs(low_slope) < 0.001:
        return "水平震荡"
    elif high_slope < 0 and low_slope > 0:
        return "收敛震荡"
    elif high_slope > 0 and low_slope < 0:
        return "扩张震荡"
    else:
        return "其他形态"

说白了,震荡识别不是目的,目的是为POV策略找到合适的「战场」。水平震荡里大胆做,收敛震荡里小心做,扩张震荡里选择性做——这是我个人的经验法则。

总结一下:震荡行情识别,核心是看波动率(ATR/布林带宽度)和形态(水平/收敛/扩张)。这两个维度交叉,基本能覆盖90%的震荡场景。剩下的10%,交给经验和盘感。
震荡行情识别知识体系 震荡行情识别 定义 量化指标 分类 价格区间波动 无明显趋势 ATR 布林带宽度 水平震荡 收敛/扩张 20日ATR + 百分位 布林带宽度 + 阈值

最后说一句:震荡识别不是一锤子买卖。市场是动态的,今天震荡明天可能就趋势了。我建议把震荡识别做成一个滚动窗口,每根K线都重新计算一次。这样POV策略才能及时调整参数,跟上市场的节奏。

专注资料整理