Momentum:SQUEEZE_PRO(多尺度波动挤压指标)技术指标详解
一、SQUEEZE_PRO的定义
SQUEEZE_PRO(多尺度波动挤压指标,简称SQZPRO) 是John Carter经典TTM Squeeze指标的升级版本。该指标同样基于John Carter所著的《Mastering the Trade》第11章中讨论的TTM Squeeze概念,通过引入三档肯特纳通道(宽/正常/窄) 来细分波动压缩的等级,帮助交易者更早识别潜在的爆发性行情。
核心思想
经典Squeeze指标仅判断布林带是否完全落入肯特纳通道内部,这种单一的二元判断可能遗漏“轻度压缩”带来的提前信号。SQUEEZE_PRO通过设置三组不同宽度的肯特纳通道(宽通道乘数2.0、正常通道乘数1.5、窄通道乘数1.0),将波动压缩程度细分为三级预警:
- 窄挤压(Narrow Squeeze):最敏感,通常是行情发酵初期,波动已经开始收敛
- 正常挤压(Normal Squeeze):波动进一步压缩,爆发概率上升
- 宽挤压(Wide Squeeze):布林带极度收缩,往往是“临门一脚”的引爆点
同时,SQUEEZE_PRO通过动量柱(Momentum Histogram)显示价格动能的方向和强度,帮助交易者判断突破方向。
SQUEEZE vs SQUEEZE_PRO
| 肯特纳通道层级 | 单层(乘数1.5) | 三层(乘数2.0/1.5/1.0) |
| 挤压细分程度 | 二元判断(开/关) | 三级预警(宽/正常/窄) |
| 预警能力 | 单一阶段 | 逐级递进,提前预警 |
| 信号可靠性 | 中等 | 更高(多重确认) |
SQUEEZE_PRO的核心组件
| 多尺度动量柱 | SQZPRO | 平滑后的动量值,正值多头、负值空头,绝对值越大动能越强 |
| 宽挤压信号 | SQZPRO_ON_WIDE | 布林带完全落入宽KC(2×ATR),波动率极低,红灯预警 |
| 正常挤压信号 | SQZPRO_ON_NORMAL | 布林带落入正常KC(1.5×ATR),压缩加剧黄灯预警 |
| 窄挤压信号 | SQZPRO_ON_NARROW | 布林带落入窄KC(1×ATR),轻度压缩绿灯闪烁 |
| 挤压释放信号 | SQZPRO_OFF | 布林带折返并突破最宽KC,动能即将爆发 |
| 无挤压状态 | SQZPRO_NO | 市场正常波动,三档信号均为0 |
二、SQUEEZE_PRO的计算方法
1. 核心计算框架
SQUEEZE_PRO的计算包含四个层次:布林带计算、三档肯特纳通道计算、挤压状态判断、动量柱计算。
2. 详细计算步骤
步骤一:计算布林带(Bollinger Bands)
设周期bb_length=20,标准差倍数bb_std=2.0:
首先计算20周期简单移动平均线(SMA):
BB_MIDt=SMA20(Close)t
\\mathrm{BB\\_MID}_t = \\mathrm{SMA}_{20}(\\mathrm{Close})t
BB_MIDt=SMA20(Close)t
计算20周期收盘价的标准差:
σt=120∑i=019(Closet−i−BB_MIDt)2
\\sigma_t = \\sqrt{\\frac{1}{20} \\sum_{i=0}^{19} (\\mathrm{Close}_{t-i} – \\mathrm{BB\\_MID}_t)^2}
σt=201i=0∑19(Closet−i−BB_MIDt)2
布林带上下轨:
BB_HIGHt=BB_MIDt+2.0×σt
\\mathrm{BB\\_HIGH}_t = \\mathrm{BB\\_MID}_t + 2.0 \\times \\sigma_t
BB_HIGHt=BB_MIDt+2.0×σt
BB_LOWt=BB_MIDt−2.0×σt
\\mathrm{BB\\_LOW}_t = \\mathrm{BB\\_MID}_t – 2.0 \\times \\sigma_t
BB_LOWt=BB_MIDt−2.0×σt
步骤二:计算三档肯特纳通道(Keltner Channels)
肯特纳通道以20周期指数移动平均线(EMA)为中轴:
KC_MIDt=EMA20(Close)t
\\mathrm{KC\\_MID}_t = \\mathrm{EMA}_{20}(\\mathrm{Close})_t
KC_MIDt=EMA20(Close)t
首先计算真实波幅(True Range):
TRt=max(Ht−Lt,∣Ht−Ct−1∣,∣Lt−Ct−1∣)
\\mathrm{TR}_t = \\max(H_t – L_t, |H_t – C_{t-1}|, |L_t – C_{t-1}|)
TRt=max(Ht−Lt,∣Ht−Ct−1∣,∣Lt−Ct−1∣)
计算20周期平均真实波幅(使用EMA):
ATR20,t=EMA20(TR)t
\\mathrm{ATR}_{20,t} = \\mathrm{EMA}_{20}(\\mathrm{TR})_t
ATR20,t=EMA20(TR)t
三档通道的上下轨计算(分别对应乘数 k=2.0,1.5,1.0k = 2.0, 1.5, 1.0k=2.0,1.5,1.0):
宽通道(Wide):乘数 kwide=2.0k_{\\mathrm{wide}} = 2.0kwide=2.0
KC_HIGHwide,t=KC_MIDt+2.0×ATR20,t
\\mathrm{KC\\_HIGH}_{\\mathrm{wide},t} = \\mathrm{KC\\_MID}_t + 2.0 \\times \\mathrm{ATR}_{20,t}
KC_HIGHwide,t=KC_MIDt+2.0×ATR20,t
KC_LOWwide,t=KC_MIDt−2.0×ATR20,t
\\mathrm{KC\\_LOW}_{\\mathrm{wide},t} = \\mathrm{KC\\_MID}_t – 2.0 \\times \\mathrm{ATR}_{20,t}
KC_LOWwide,t=KC_MIDt−2.0×ATR20,t
正常通道(Normal):乘数 knormal=1.5k_{\\mathrm{normal}} = 1.5knormal=1.5
KC_HIGHnormal,t=KC_MIDt+1.5×ATR20,t
\\mathrm{KC\\_HIGH}_{\\mathrm{normal},t} = \\mathrm{KC\\_MID}_t + 1.5 \\times \\mathrm{ATR}_{20,t}
KC_HIGHnormal,t=KC_MIDt+1.5×ATR20,t
KC_LOWnormal,t=KC_MIDt−1.5×ATR20,t
\\mathrm{KC\\_LOW}_{\\mathrm{normal},t} = \\mathrm{KC\\_MID}_t – 1.5 \\times \\mathrm{ATR}_{20,t}
KC_LOWnormal,t=KC_MIDt−1.5×ATR20,t
窄通道(Narrow):乘数 knarrow=1.0k_{\\mathrm{narrow}} = 1.0knarrow=1.0
KC_HIGHnarrow,t=KC_MIDt+1.0×ATR20,t
\\mathrm{KC\\_HIGH}_{\\mathrm{narrow},t} = \\mathrm{KC\\_MID}_t + 1.0 \\times \\mathrm{ATR}_{20,t}
KC_HIGHnarrow,t=KC_MIDt+1.0×ATR20,t
KC_LOWnarrow,t=KC_MIDt−1.0×ATR20,t
\\mathrm{KC\\_LOW}_{\\mathrm{narrow},t} = \\mathrm{KC\\_MID}_t – 1.0 \\times \\mathrm{ATR}_{20,t}
KC_LOWnarrow,t=KC_MIDt−1.0×ATR20,t
步骤三:计算三级挤压状态
挤压状态的判断基于布林带与三档肯特纳通道的相对位置:
宽挤压开启(SQZPRO_ON_WIDE = 1):布林带完全落入宽KC通道内部
SQZPRO_ON_WIDEt=1{BB_LOWt>KC_LOWwide,t∧BB_HIGHt<KC_HIGHwide,t}
\\mathrm{SQZPRO\\_ON\\_WIDE}_t = \\mathbf{1}_{\\{\\mathrm{BB\\_LOW}_t > \\mathrm{KC\\_LOW}_{\\mathrm{wide},t} \\land \\mathrm{BB\\_HIGH}_t < \\mathrm{KC\\_HIGH}_{\\mathrm{wide},t}\\}}
SQZPRO_ON_WIDEt=1{BB_LOWt>KC_LOWwide,t∧BB_HIGHt<KC_HIGHwide,t}
正常挤压开启(SQZPRO_ON_NORMAL = 1):布林带落入正常KC通道内部
SQZPRO_ON_NORMALt=1{BB_LOWt>KC_LOWnormal,t∧BB_HIGHt<KC_HIGHnormal,t}
\\mathrm{SQZPRO\\_ON\\_NORMAL}_t = \\mathbf{1}_{\\{\\mathrm{BB\\_LOW}_t > \\mathrm{KC\\_LOW}_{\\mathrm{normal},t} \\land \\mathrm{BB\\_HIGH}_t < \\mathrm{KC\\_HIGH}_{\\mathrm{normal},t}\\}}
SQZPRO_ON_NORMALt=1{BB_LOWt>KC_LOWnormal,t∧BB_HIGHt<KC_HIGHnormal,t}
窄挤压开启(SQZPRO_ON_NARROW = 1):布林带落入窄KC通道内部
SQZPRO_ON_NARROWt=1{BB_LOWt>KC_LOWnarrow,t∧BB_HIGHt<KC_HIGHnarrow,t}
\\mathrm{SQZPRO\\_ON\\_NARROW}_t = \\mathbf{1}_{\\{\\mathrm{BB\\_LOW}_t > \\mathrm{KC\\_LOW}_{\\mathrm{narrow},t} \\land \\mathrm{BB\\_HIGH}_t < \\mathrm{KC\\_HIGH}_{\\mathrm{narrow},t}\\}}
SQZPRO_ON_NARROWt=1{BB_LOWt>KC_LOWnarrow,t∧BB_HIGHt<KC_HIGHnarrow,t}
挤压释放(SQZPRO_OFF = 1):布林带突破宽KC通道边界
SQZPRO_OFFt=1{BB_LOWt<KC_LOWwide,t∨BB_HIGHt>KC_HIGHwide,t}
\\mathrm{SQZPRO\\_OFF}_t = \\mathbf{1}_{\\{\\mathrm{BB\\_LOW}_t < \\mathrm{KC\\_LOW}_{\\mathrm{wide},t} \\lor \\mathrm{BB\\_HIGH}_t > \\mathrm{KC\\_HIGH}_{\\mathrm{wide},t}\\}}
SQZPRO_OFFt=1{BB_LOWt<KC_LOWwide,t∨BB_HIGHt>KC_HIGHwide,t}
无挤压状态(SQZPRO_NO = 1):不满足上述任何条件
SQZPRO_NOt=1{−SQZPRO_ON_WIDE∧−SQZPRO_ON_NORMAL∧−SQZPRO_ON_NARROW∧−SQZPRO_OFF}
\\mathrm{SQZPRO\\_NO}_t = \\mathbf{1}_{\\{-\\mathrm{SQZPRO\\_ON\\_WIDE} \\land -\\mathrm{SQZPRO\\_ON\\_NORMAL} \\land -\\mathrm{SQZPRO\\_ON\\_NARROW} \\land -\\mathrm{SQZPRO\\_OFF}\\}}
SQZPRO_NOt=1{−SQZPRO_ON_WIDE∧−SQZPRO_ON_NORMAL∧−SQZPRO_ON_NARROW∧−SQZPRO_OFF}
注意:同一根K线只会出现一类启动信号(窄/正常/宽),或处于OFF/NO状态之一。
步骤四:计算多尺度动量柱(SQZPRO)
动量柱的计算基于动量指标,然后进行平滑处理:
首先计算动量值(周期mom_length=12):
MOMt=Closet−Closet−11
\\mathrm{MOM}_t = \\mathrm{Close}_t − \\mathrm{Close}_{t−11}
MOMt=Closet−Closet−11
对MOM进行指数移动平均平滑(mom_smooth=6):
SQZPROt=EMA6(MOM)t
\\mathrm{SQZPRO}_t = \\mathrm{EMA}_6(\\mathrm{MOM})t
SQZPROt=EMA6(MOM)t
3. 参数说明
| bb_length | int | 20 | 布林带计算周期 |
| bb_std | float | 2.0 | 布林带标准差倍数 |
| kc_length | int | 20 | 肯特纳通道计算周期 |
| kc_scalar_wide | float | 2.0 | 宽肯特纳通道ATR乘数 |
| kc_scalar_normal | float | 1.5 | 正常肯特纳通道ATR乘数 |
| kc_scalar_narrow | float | 1.0 | 窄肯特纳通道ATR乘数 |
| mom_length | int | 12 | 动量计算周期 |
| mom_smooth | int | 6 | 动量平滑周期 |
| use_tr | bool | True | 是否使用真实波幅 |
| mamode | str | “sma” | 动量平滑类型(‘ema’或’sma’) |
三、SQUEEZE_PRO的使用方法
1. “三级爆破”交易流程
SQUEEZE_PRO的设计逻辑是“波动压缩等级递进→爆发确认”的完整周期。
| ① 窄挤压(绿灯) | SQZPRO_ON_NARROW=1 | 波动开始收敛,多空力量趋于平衡,行情正在酝酿初期 | 保持关注,开始筛选交易机会 |
| ② 正常挤压(黄灯) | SQZPRO_ON_NORMAL=1 | 多空双方进入拉锯,波动进一步压缩,爆发概率上升 | 准备交易计划,设置预警 |
| ③ 宽挤压(红灯) | SQZPRO_ON_WIDE=1 | 波动率极低,“低压锅”达到极限,随时可能引爆 | 密切关注,等待突破信号 |
| ④ 爆发(释放) | SQZPRO_OFF=1 | 布林带突破最宽KC,动能爆发 | 执行顺势交易 |
这一流程的核心逻辑像交通信号灯——红灯前亮起黄灯让你提前热身,而非等到最后一秒才冲刺。
2. 方向判断:动量柱 + 挤压释放
挤压释放(SQZPRO_OFF=1)本身只代表波动率将扩张,但扩张方向并不确定。必须结合动量柱(SQZPRO)的方向来判断:
| SQZPRO_OFF=1 + 动量柱为正值(SQZPRO>0) | 多头动能爆发,上涨趋势启动 | 做多 |
| SQZPRO_OFF=1 + 动量柱为负值(SQZPRO<0) | 空头动能爆发,下跌趋势启动 | 做空 |
3. 动量柱的趋势强度判断
动量柱的数值大小反映趋势强度,绝对值越大,动能越强:
| 正值且持续扩大(绿色柱) | 多头动能持续增强 | 顺势持仓 |
| 正值但持续缩小 | 多头动能减弱,上涨乏力 | 减仓预警 |
| 负值且持续扩大(红色柱) | 空头动能持续增强 | 顺势持仓 |
| 负值但持续缩小 | 空头动能减弱,下跌放缓 | 空头平仓预警 |
4. 三级挤压的实战意义
不同层级的挤压状态具有不同的信号价值:
- 窄挤压(最敏感):波动已经收敛到窄KC内部,往往是趋势启动的“先遣信号”,适合激进的交易者提前布局
- 正常挤压(较可靠):波动进一步压缩,出现后胜率较窄挤压更高,适合大多数交易者关注
- 宽挤压(最强信号):布林带完全被宽KC包裹,波动率极低,“弹簧”压缩到极致,一旦释放往往伴随剧烈单边行情
5. 多重过滤策略
为提高信号可靠性,建议加入额外的过滤条件:
均线过滤:
- 做多信号:SQZPRO_OFF=1 + 动量柱>0 + 价格>MA50
- 做空信号:SQZPRO_OFF=1 + 动量柱<0 + 价格<MA50
ADX过滤:
- ADX<20时,市场处于震荡,挤压信号假突破概率较高
- ADX>25时,市场处于趋势状态,挤压信号可靠性更高
成交量验证:
- 挤压释放时若伴随成交量明显放大,突破的可信度显著提高
6. 注意事项与局限性
使用SQUEEZE_PRO前需了解以下要点:
| 滞后性质 | 挤压释放确认信号出现时,价格往往已启动一段距离 |
| 震荡市假信号 | 在趋势不明确的市场中,挤压可能被反复触发,导致假突破 |
| 参数敏感 | 不同品种可能需要调整KC乘数参数以获得最佳效果 |
| 需要组合使用 | 建议与均线系统、ADX趋势强度指标配合使用 |
四、使用pandas_ta计算SQUEEZE_PRO(附示例代码)
1. pandas_ta中的SQUEEZE_PRO函数
pandas_ta库内置了SQUEEZE_PRO指标的完整实现,位于momentum模块中。
2. 函数完整参数
pandas_ta.momentum.squeeze_pro(
high, low, close,
bb_length=None, bb_std=None,
kc_length=None,
kc_scalar_wide=None, kc_scalar_normal=None, kc_scalar_narrow=None,
mom_length=None, mom_smooth=None,
use_tr=None, mamode=None,
offset=None, **kwargs
)
参数详解:
| high | pd.Series | 必需 | 最高价序列 |
| low | pd.Series | 必需 | 最低价序列 |
| close | pd.Series | 必需 | 收盘价序列 |
| bb_length | int | 20 | 布林带计算周期 |
| bb_std | float | 2.0 | 布林带标准差倍数 |
| kc_length | int | 20 | 肯特纳通道周期 |
| kc_scalar_wide | float | 2.0 | 宽通道ATR乘数 |
| kc_scalar_normal | float | 1.5 | 正常通道ATR乘数 |
| kc_scalar_narrow | float | 1.0 | 窄通道ATR乘数 |
| mom_length | int | 12 | 动量计算周期 |
| mom_smooth | int | 6 | 动量平滑周期 |
| use_tr | bool | True | 是否使用真实波幅(否则用High-Low) |
| mamode | str | “sma” | 移动平均类型(‘ema’或’sma’) |
返回值:pd.DataFrame——包含6列:
- SQZPRO_{params}:多尺度动量柱(正值多头,负值空头)
- SQZPRO_ON_WIDE:宽挤压启动(1=红灯/极度压缩)
- SQZPRO_ON_NORMAL:正常挤压启动(1=黄灯/压缩加剧)
- SQZPRO_ON_NARROW:窄挤压启动(1=绿灯/轻度压缩)
- SQZPRO_OFF:挤压释放(1=爆发信号)
- SQZPRO_NO:无挤压状态(1=正常波动)
3. 示例代码
import pandas as pd
import pandas_ta as ta
import numpy as np
import matplotlib.pyplot as plt
# ========== 第一步:准备数据 ==========
np.random.seed(42)
dates = pd.date_range(start='2023-01-01', end='2024-12-31', freq='D')
n = len(dates)
# 生成带趋势和周期波动的价格序列(包含低波动区间)
trend = np.linspace(0, 35, n)
cycle = np.sin(np.linspace(0, 4 * np.pi, n)) * 12
noise = np.random.randn(n) * 2
price_series = 100 + trend + cycle + noise
# 添加一个低波动率区间(模拟挤压状态)
low_vol_start = 300
low_vol_end = 400
price_series[low_vol_start:low_vol_end] = price_series[low_vol_start] + np.random.randn(low_vol_end – low_vol_start) * 1
df = pd.DataFrame(index=dates[:n])
df['close'] = price_series[:n]
df['high'] = df['close'] + np.abs(np.random.randn(n)) * 2 + 1
df['low'] = df['close'] – np.abs(np.random.randn(n)) * 2 – 1
df['volume'] = np.random.randint(5000000, 30000000, n)
print("=" * 70)
print("数据预览:")
print(df.head())
print("\\n" + "=" * 70 + "\\n")
# ========== 第二步:计算SQUEEZE_PRO(基础用法) ==========
# 使用默认参数(BB:20,2 | KC:20, 宽2/正常1.5/窄1 | 动量:12,6)
sqzpro_df = ta.squeeze_pro(df['high'], df['low'], df['close'])
# 查看返回的DataFrame结构
print("SQUEEZE_PRO返回列名称:")
print(sqzpro_df.columns.tolist())
print("\\n" + "=" * 70 + "\\n")
# 将计算结果添加到原DataFrame
# 根据列名特征提取各列
sqzpro_col = [col for col in sqzpro_df.columns if col.startswith('SQZPRO_') and '_ON_' not in col and '_OFF' not in col and '_NO' not in col]
sqzpro_on_wide_col = [col for col in sqzpro_df.columns if col.endswith('_ON_WIDE')]
sqzpro_on_normal_col = [col for col in sqzpro_df.columns if col.endswith('_ON_NORMAL')]
sqzpro_on_narrow_col = [col for col in sqzpro_df.columns if col.endswith('_ON_NARROW')]
sqzpro_off_col = [col for col in sqzpro_df.columns if col.endswith('_OFF')]
sqzpro_no_col = [col for col in sqzpro_df.columns if col.endswith('_NO')]
df['SQZPRO'] = sqzpro_df[sqzpro_col[0]] if sqzpro_col else sqzpro_df.iloc[:, 0]
df['SQZPRO_ON_WIDE'] = sqzpro_df[sqzpro_on_wide_col[0]] if sqzpro_on_wide_col else sqzpro_df.iloc[:, 1]
df['SQZPRO_ON_NORMAL'] = sqzpro_df[sqzpro_on_normal_col[0]] if sqzpro_on_normal_col else sqzpro_df.iloc[:, 2]
df['SQZPRO_ON_NARROW'] = sqzpro_df[sqzpro_on_narrow_col[0]] if sqzpro_on_narrow_col else sqzpro_df.iloc[:, 3]
df['SQZPRO_OFF'] = sqzpro_df[sqzpro_off_col[0]] if sqzpro_off_col else sqzpro_df.iloc[:, 4]
df['SQZPRO_NO'] = sqzpro_df[sqzpro_no_col[0]] if sqzpro_no_col else sqzpro_df.iloc[:, 5]
print("SQUEEZE_PRO计算结果(最近10行):")
print(df[['close', 'SQZPRO', 'SQZPRO_ON_WIDE', 'SQZPRO_ON_NORMAL', 'SQZPRO_ON_NARROW', 'SQZPRO_OFF']].tail(10))
print("\\n" + "=" * 70 + "\\n")
# ========== 第三步:手动验证SQUEEZE_PRO核心逻辑 ==========
def manual_squeeze_pro(high, low, close,
bb_length=20, bb_std=2.0,
kc_length=20,
kc_scalar_wide=2.0, kc_scalar_normal=1.5, kc_scalar_narrow=1.0,
mom_length=12, mom_smooth=6):
"""手动验证SQUEEZE_PRO核心计算逻辑"""
# 1. 计算布林带
sma = close.rolling(window=bb_length).mean()
std = close.rolling(window=bb_length).std()
bb_high = sma + bb_std * std
bb_low = sma – bb_std * std
# 2. 计算ATR(平均真实波幅)
tr1 = high – low
tr2 = abs(high – close.shift(1))
tr3 = abs(low – close.shift(1))
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
atr = tr.ewm(span=kc_length, adjust=False).mean()
# 3. 计算肯特纳通道中轨
kc_mid = close.ewm(span=kc_length, adjust=False).mean()
# 4. 计算三档肯特纳通道
kc_high_wide = kc_mid + kc_scalar_wide * atr
kc_low_wide = kc_mid – kc_scalar_wide * atr
kc_high_normal = kc_mid + kc_scalar_normal * atr
kc_low_normal = kc_mid – kc_scalar_normal * atr
kc_high_narrow = kc_mid + kc_scalar_narrow * atr
kc_low_narrow = kc_mid – kc_scalar_narrow * atr
# 5. 判断三级挤压状态
sqz_on_wide = ((bb_low > kc_low_wide) & (bb_high < kc_high_wide)).astype(int)
sqz_on_normal = ((bb_low > kc_low_normal) & (bb_high < kc_high_normal)).astype(int)
sqz_on_narrow = ((bb_low > kc_low_narrow) & (bb_high < kc_high_narrow)).astype(int)
sqz_off = ((bb_low < kc_low_wide) | (bb_high > kc_high_wide)).astype(int)
sqz_no = ((sqz_on_wide == 0) & (sqz_on_normal == 0) & (sqz_on_narrow == 0) & (sqz_off == 0)).astype(int)
# 6. 计算动量柱
mom = close – close.shift(mom_length – 1)
sqzpro = mom.ewm(span=mom_smooth, adjust=False).mean()
return sqzpro, sqz_on_wide, sqz_on_normal, sqz_on_narrow, sqz_off, sqz_no
df['SQZPRO_manual'], df['WIDE_manual'], df['NORMAL_manual'], df['NARROW_manual'], df['OFF_manual'], df['NO_manual'] = manual_squeeze_pro(
df['high'], df['low'], df['close']
)
# 验证一致性
diff_sqz = (df['SQZPRO'] – df['SQZPRO_manual']).abs().max()
print(f"pandas_ta与手动计算动量值的最大差异:{diff_sqz:.10f}")
print("\\n" + "=" * 70 + "\\n")
# ========== 第五步:挤压状态统计 ==========
print("挤压状态统计:")
print(f"窄挤压(轻度压缩-绿灯)天数:{df['SQZPRO_ON_NARROW'].sum()}")
print(f"正常挤压(中度压缩-黄灯)天数:{df['SQZPRO_ON_NORMAL'].sum()}")
print(f"宽挤压(极度压缩-红灯)天数:{df['SQZPRO_ON_WIDE'].sum()}")
print(f"挤压释放(爆发)天数:{df['SQZPRO_OFF'].sum()}")
print(f"无挤压天数:{df['SQZPRO_NO'].sum()}")
print("\\n" + "=" * 70 + "\\n")
# ========== 第六步:生成交易信号 ==========
# 买入信号:挤压释放 + 动量柱为正
df['buy_signal'] = (df['SQZPRO_OFF'] == 1) & (df['SQZPRO'] > 0)
# 卖出信号:挤压释放 + 动量柱为负
df['sell_signal'] = (df['SQZPRO_OFF'] == 1) & (df['SQZPRO'] < 0)
# 动能强度分类
df['momentum_strength'] = ''
df.loc[df['SQZPRO'] > 2, 'momentum_strength'] = '强多头动能'
df.loc[(df['SQZPRO'] > 0) & (df['SQZPRO'] <= 2), 'momentum_strength'] = '弱多头动能'
df.loc[(df['SQZPRO'] < 0) & (df['SQZPRO'] >= –2), 'momentum_strength'] = '弱空头动能'
df.loc[df['SQZPRO'] < –2, 'momentum_strength'] = '强空头动能'
print("交易信号统计:")
print(f"买入信号(挤压释放+多头动量)数量:{df['buy_signal'].sum()}")
print(f"卖出信号(挤压释放+空头动量)数量:{df['sell_signal'].sum()}")
print("\\n最近10个交易信号:")
signals = df[df['buy_signal'] | df['sell_signal']].tail(10)
if not signals.empty:
for idx, row in signals.iterrows():
signal_type = "买入" if row['buy_signal'] else "卖出"
squeeze_level = []
if row['SQZPRO_ON_NARROW']: squeeze_level.append("窄挤压")
if row['SQZPRO_ON_NORMAL']: squeeze_level.append("正常挤压")
if row['SQZPRO_ON_WIDE']: squeeze_level.append("宽挤压")
level_str = "+".join(squeeze_level) if squeeze_level else "无"
print(f"{idx.date()} – {signal_type} – SQZPRO: {row['SQZPRO']:.2f} – 挤压等级: {level_str}")
print("\\n" + "=" * 70 + "\\n")
# ========== 第七步:策略回测(挤压突破策略) ==========
# 策略:买入信号出现时买入,持有至卖出信号或动能转弱
df['position'] = 0
in_position = False
for i in range(1, len(df)):
if not in_position and df['buy_signal'].iloc[i]:
df.loc[df.index[i], 'position'] = 1
in_position = True
elif in_position and (df['sell_signal'].iloc[i] or df['momentum_strength'].iloc[i] == '弱多头动能'):
df.loc[df.index[i], 'position'] = 0
in_position = False
else:
df.loc[df.index[i], 'position'] = df['position'].iloc[i–1]
# 计算收益
df['returns'] = df['close'].pct_change()
df['strategy_returns'] = df['position'].shift(1) * df['returns']
total_return_buyhold = (1 + df['returns']).prod() – 1
total_return_strategy = (1 + df['strategy_returns']).prod() – 1
win_rate = (df['strategy_returns'] > 0).sum() / (df['strategy_returns'] != 0).sum() if (df['strategy_returns'] != 0).sum() > 0 else 0
print("=" * 70)
print("策略绩效统计(SQUEEZE_PRO三级挤压突破策略回测):")
print(f"买入持有策略总收益率:{total_return_buyhold:.2%}")
print(f"SQUEEZE_PRO策略总收益率:{total_return_strategy:.2%}")
print(f"策略胜率:{win_rate:.2%}")
print("\\n注意:此为简化回测,仅供参考;实盘需考虑手续费、滑点等因素")
print("=" * 70 + "\\n")
# ========== 第八步:可视化 ==========
plt.figure(figsize=(14, 12))
# 子图1:价格走势 + 挤压状态标记
plt.subplot(3, 1, 1)
plt.plot(df.index[–200:], df['close'][–200:], label='Close Price',
linewidth=1.5, color='black')
# 标记不同等级的挤压状态
narrow_points = df[df['SQZPRO_ON_NARROW'] == 1]
normal_points = df[df['SQZPRO_ON_NORMAL'] == 1]
wide_points = df[df['SQZPRO_ON_WIDE'] == 1]
plt.scatter(narrow_points.index[–200:], narrow_points['close'][–200:],
color='green', s=15, label='窄挤压(绿灯)', alpha=0.7)
plt.scatter(normal_points.index[–200:], normal_points['close'][–200:],
color='yellow', s=20, label='正常挤压(黄灯)', alpha=0.7)
plt.scatter(wide_points.index[–200:], wide_points['close'][–200:],
color='red', s=25, label='宽挤压(红灯)', alpha=0.7)
plt.title('Price Chart with Squeeze Pro Signals (Last 200 days)', fontsize=14)
plt.ylabel('Price')
plt.legend()
plt.grid(True, alpha=0.3)
# 子图2:挤压状态指示(点阵图)
plt.subplot(3, 1, 2)
plt.plot(df.index[–200:], df['SQZPRO_ON_NARROW'][–200:], 'g.', markersize=3, label='窄挤压(绿灯)', alpha=0.7)
plt.plot(df.index[–200:], df['SQZPRO_ON_NORMAL'][–200:], 'y.', markersize=4, label='正常挤压(黄灯)', alpha=0.7)
plt.plot(df.index[–200:], df['SQZPRO_ON_WIDE'][–200:], 'r.', markersize=5, label='宽挤压(红灯)', alpha=0.7)
plt.plot(df.index[–200:], df['SQZPRO_OFF'][–200:], 'bo', markersize=4, label='挤压释放(爆发)', alpha=0.7)
plt.ylim(–0.1, 1.1)
plt.title('Squeeze Pro State: Green=Narrow, Yellow=Normal, Red=Wide, Blue=Release', fontsize=12)
plt.ylabel('State')
plt.legend(loc='upper left')
plt.grid(True, alpha=0.3)
# 子图3:多尺度动量柱
plt.subplot(3, 1, 3)
colors = ['green' if val >= 0 else 'red' for val in df['SQZPRO'][–200:]]
plt.bar(df.index[–200:], df['SQZPRO'][–200:], color=colors, alpha=0.7, width=0.8)
plt.axhline(y=0, color='gray', linestyle='-', linewidth=1)
plt.title('SQZPRO Momentum Histogram: 多头动能(绿色) vs 空头动能(红色)', fontsize=12)
plt.xlabel('Date')
plt.ylabel('SQZPRO Momentum')
plt.grid(True, alpha=0.3)
# 标记买入/卖出信号
buy_signals = df[df['buy_signal']]
sell_signals = df[df['sell_signal']]
plt.scatter(buy_signals.index[–20:], [0.5] * min(20, len(buy_signals)),
color='green', marker='^', s=80, label='Buy Signal', alpha=0.8)
plt.scatter(sell_signals.index[–20:], [0.5] * min(20, len(sell_signals)),
color='red', marker='v', s=80, label='Sell Signal', alpha=0.8)
plt.tight_layout()
plt.show()
# ========== 第九步:数据清洗提示 ==========
nan_count = df['SQZPRO'].isna().sum()
print(f"\\nSQUEEZE_PRO初始NaN数量:{nan_count}")
print("原因:SQUEEZE_PRO需要至少max(bb_length, kc_length, mom_length)个周期的数据")
print("处理建议:df_clean = df.iloc[20:].copy()")
print("\\n" + "=" * 70)
print("SQUEEZE_PRO使用提示:")
print("1. 三级挤压递进:窄挤压(绿灯)→正常挤压(黄灯)→宽挤压(红灯)")
print("2. 挤压释放(蓝点)+ 动量柱为正 = 多头突破信号")
print("3. 挤压释放(蓝点)+ 动量柱为负 = 空头突破信号")
print("4. 建议配合均线过滤,如价格>MA50时做多,<MA50时做空")
print("5. 在ADX<20的震荡市中,挤压信号假突破概率较高,谨慎使用")
print("=" * 70)
五、总结
SQUEEZE_PRO(多尺度波动挤压指标)是对经典Squeeze指标的全面升级,其核心价值与定位如下:
| 核心创新 | 引入三档肯特纳通道(宽/正常/窄),细分波动压缩等级 |
| 三级预警 | 窄挤压(绿灯)→正常挤压(黄灯)→宽挤压(红灯) |
| 核心公式 | BB完全落入KC_Narrow/Normal/Wide → 对应等级挤压 |
| 六组件系统 | 动量柱 + 宽/正常/窄三档挤压 + 释放 + 无挤压 |
| 默认参数 | (20,2,20,2.0,1.5,1.0,12,6) |
| 最佳应用场景 | 趋势品种的爆发点识别、多空切换判断 |
| 主要局限 | 滞后性、震荡市假信号、需配合其他指标 |
实战使用三原则:
最后提醒:SQUEEZE_PRO的核心价值在于将“波动压缩”这一连续过程量化为逐级递进的预警信号,帮助交易者提前布局而非被动等待。它本质上回答了交易中的核心问题:“弹簧”压缩到什么程度了?什么时候可能爆发? 然而,如所有技术指标一样,SQUEEZE_PRO并非圣杯——在震荡市中假突破信号仍然存在。建议将其作为完整交易系统的“触发器”,配合趋势确认工具(如ADX、均线)和严格的风控管理使用。

