2. 震荡行情识别
做POV策略这些年,我最大的体会就是:趋势行情靠胆量,震荡行情靠手艺。很多朋友在震荡市里亏钱,不是策略不对,而是根本不知道自己正处在震荡中。今天我们就来聊聊,怎么用量化手段把震荡行情「抓」出来。
2.1 震荡行情的定义
先给个直观的定义:震荡行情,就是价格在一个区间内来回波动,没有明确的方向。说白了,多空双方在打架,谁也没打赢。
从量化角度看,我习惯用三个条件来判定:
- 价格波动幅度有限:最高价和最低价的差值,在某个百分比以内
- 无明显趋势:均线走平,或者短期均线围绕长期均线上下穿梭
- 成交量相对萎缩:没有放量突破的信号
嗯,这里要注意:震荡不等于「没行情」。恰恰相反,震荡行情里波动率虽然不大,但节奏感很强,非常适合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绝对值不够,要看它的相对位置。
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策略做市,结果一根大阳线直接把我打穿。
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策略找到合适的「战场」。水平震荡里大胆做,收敛震荡里小心做,扩张震荡里选择性做——这是我个人的经验法则。
最后说一句:震荡识别不是一锤子买卖。市场是动态的,今天震荡明天可能就趋势了。我建议把震荡识别做成一个滚动窗口,每根K线都重新计算一次。这样POV策略才能及时调整参数,跟上市场的节奏。