【Python量化技术指标实战 04】布林带通道突破,怎么知道是真突破还是假突破?

本文你将得到什么:

  1. 纯手工计算布林带(中轨/上轨/下轨,20日 + 2倍标准差)
  2. 用代码自动检测"收盘价上穿上轨"的突破点
  3. 用"成交量确认 + 收线位置确认"两条规则过滤假突破
  4. 双面板可视化(价格带通道 + 成交量)
  5. 可运行的完整代码

痛点:突破追进去就被套

股价冲出布林带上轨了,你追进去,第二天就回落------这就是经典的"假突破"。布林带突破策略最大的敌人不是突破本身,而是那些"摸了一下上轨就缩回去"的伪信号。

怎么区分真突破和假突破?单看价格不够,要加两个过滤器:量能 和收线位置。本文把这套逻辑写成 50 行 Python。


环境准备

bash 复制代码
pip install mairui pandas numpy matplotlib

第一步:拉取数据

python 复制代码
from mairui import Client

client = Client("LICENCE-66D8-9F96-0C7F0FBCD073")
bars = client.stock_history(code="600519", period="d", dividend="f")

import pandas as pd
df = pd.DataFrame(bars)
df = df.rename(columns={"t": "date"})
df = df.sort_values("date").reset_index(drop=True)
for col in ("o", "h", "l", "c", "v"):
    df[col] = df[col].astype(float)

说明:这里用的是公开演示证书,为演示方便明文写入;实际项目请换成你自己的正式证书(申请配置见 SDK 文档 https://github.com/MaiRuiApi/mairui ),换证后代码无需修改。


第二步:手工计算布林带

布林带三条线的定义:

  1. 中轨 = N 日收盘价的移动平均(常用 N=20)
  2. 上轨 = 中轨 + K × N 日标准差(常用 K=2)
  3. 下轨 = 中轨 - K × N 日标准差
python 复制代码
def calc_boll(df, n=20, k=2):
    mid = df["c"].rolling(window=n, min_periods=n).mean()
    std = df["c"].rolling(window=n, min_periods=n).std(ddof=0)
    upper = mid + k * std
    lower = mid - k * std
    return mid, upper, lower

mid, upper, lower = calc_boll(df)
df["mid"] = mid
df["upper"] = upper
df["lower"] = lower

last = df.iloc[-1]
print(f"MID={last['mid']:.3f}, UPPER={last['upper']:.3f}, LOWER={last['lower']:.3f}")
print(f"带宽 = {(last['upper'] - last['lower']) / last['mid'] * 100:.2f}%")

输出示例:

复制代码
MID=11.212, UPPER=12.011, LOWER=10.413
带宽 = 14.25%

带宽(上轨到下轨的距离占中轨的比例)是衡量波动率的直观指标:带宽收窄意味着行情在积蓄能量,张开意味着趋势展开。


第三步:检测上轨突破

我们定义"上穿"为:昨天的收盘价还在上轨之下,今天收盘价站上了上轨。

python 复制代码
def find_breakouts(df):
    signals = []
    for i in range(1, len(df)):
        prev = df.iloc[i - 1]
        curr = df.iloc[i]
        crossed = (prev["c"] <= prev["upper"]) and (curr["c"] > curr["upper"])
        if crossed:
            signals.append((i, curr["date"], curr["c"]))
    return signals

signals = find_breakouts(df)
print(f"突破总次数: {len(signals)}")
for idx, date, price in signals:
    print(f"{date} 上穿突破 @ {price:.2f}")

输出示例:

复制代码
突破总次数: 2

第四步:真假突破过滤------两条确认规则

光有"上穿"不够。给每个突破加两个问题:

  1. 量能确认:今天放量了吗?(当日成交量 > 5日均量)
  2. 收线确认:收盘价收在当日振幅的上半区吗?((收盘-最低)/(最高-最低) >= 0.5)

缩量上攻、尾盘回落的突破,大多撑不住。

python 复制代码
df["vol_ma5"] = df["v"].rolling(window=5, min_periods=5).mean()

def classify(df, signals):
    result = []
    for idx, date, price in signals:
        curr = df.iloc[idx]
        rng = curr["h"] - curr["l"]
        close_pos = (curr["c"] - curr["l"]) / rng if rng > 0 else 0.5
        vol_ok = curr["v"] > curr["vol_ma5"]
        close_ok = close_pos >= 0.5
        result.append({
            "date": date, "price": price,
            "vol_ok": vol_ok, "close_pos": round(close_pos, 2),
            "real": vol_ok and close_ok,
        })
    return result

for s in classify(df, signals):
    tag = "真突破候选" if s["real"] else "疑似假突破"
    print(f"{s['date']} {tag} @ {s['price']:.2f} "
          f"(量能={'过' if s['vol_ok'] else '缺'}, 收线位置={s['close_pos']})")

输出示例:

复制代码
2025-11-04 真突破候选 @ 11.67 (量能=过, 收线位置=0.8)
2025-11-14 疑似假突破 @ 11.95 (量能=过, 收线位置=0.47)

两次突破都放了量,但 11-14 那次收盘只收在振幅的 47%------冲高回落,尾盘没守住。过滤规则把它标成了疑似假突破。

一个诚实的观察 :在这个演示数据区间(整体震荡下行),两个突破信号之后 5 日都是负收益------真突破候选 -5.91%,疑似假突破 -9.12%。过滤规则没有"点石成金"的魔力,它做的是相对排序:把更差的信号排到后面。任何突破策略都要先在自己的数据上回测,再谈使用。


第五步:双面板可视化

python 复制代码
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"]
plt.rcParams["axes.unicode_minus"] = False

fig, axes = plt.subplots(2, 1, figsize=(12, 8),
                         gridspec_kw={"height_ratios": [3, 1]})
x = range(len(df))

# 面板1:价格 + 布林带 + 突破标记
ax1 = axes[0]
ax1.plot(x, df["c"], label="收盘价", color="black", linewidth=0.8)
ax1.plot(x, df["mid"], label="中轨 MA20", color="blue", linewidth=1)
ax1.plot(x, df["upper"], label="上轨", color="red", linewidth=1, linestyle="--")
ax1.plot(x, df["lower"], label="下轨", color="green", linewidth=1, linestyle="--")
ax1.fill_between(x, df["lower"], df["upper"], color="blue", alpha=0.06)
ax1.set_title("布林带与突破信号")
ax1.legend()
ax1.grid(True, alpha=0.3)

# 面板2:成交量 + 5日均量
ax2 = axes[1]
colors = ["red" if c >= o else "green" for o, c in zip(df["o"], df["c"])]
ax2.bar(x, df["v"], color=colors, alpha=0.6)
ax2.plot(x, df["vol_ma5"], color="orange", linewidth=1, label="5日均量")
ax2.set_title("成交量")
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("boll_breakout.png", dpi=150)

成交量面板用 A 股惯例配色:红涨绿跌。放量突破在图上一眼就能和缩量突破区分开。


常见坑与注意

  1. 标准差用总体口径(ddof=0) :pandas 的 rolling().std() 默认 ddof=1(样本口径),与主流行情软件的布林带略有差异。想和软件对齐就显式传 ddof=0。
  2. 突破方向别只看上轨:下轨跌破同样是信号(往往是恐慌出清),本文只演示上穿,逻辑对称可自行扩展。
  3. 带宽收窄是前置信号:Squeeze(挤压)之后常有大幅波动,但方向不定------不要把收窄直接当成上涨信号。
  4. 演示数据 vs 真实数据:本文用演示证书跑通逻辑,数据为确定性演示序列;换正式证书后即为真实行情,代码无需修改。

小结

本文完成了:

  • 手工计算布林带(MID/UPPER/LOWER + 带宽)
  • 上轨突破自动检测
  • 量能 + 收线双确认的真假突破过滤
  • 双面板可视化

核心观点:突破信号本身不值钱,确认条件才是策略的护城河。下一篇我们换一个视角------不看价格指标,改看成交量本身:OBV 与量价背离的 Python 实现。


免责声明

本文仅为技术教学,不构成任何投资建议。投资有风险,入市需谨慎。


代码与文档:https://github.com/MaiRuiApi

相关推荐
未秃头的程序猿1 小时前
同一个商户号,V2 免密代扣和 V3 分账的证书怎么共存?
java·后端·微信
Soneswy1 小时前
C++基础篇——类与对象(上)
开发语言·c++
kiracrimson1 小时前
STL简单引入:把数据结构做成模板
开发语言·数据结构·c++
fanruitian1 小时前
python property
开发语言·python
captain3761 小时前
Spring Boot的配置文件
java·spring boot·后端
captain3761 小时前
Spring Boot日志
java·数据库·spring boot
Web3&Basketball1 小时前
MiniMax M3.1 Flash实战:3个成本坑
python·大模型·成本优化·minimax·推理优化·token计费
曹牧1 小时前
C#里引用ActiveX控件
开发语言·c#
Angeldream1231 小时前
第 7 章 医学影像特征提取与分析
图像处理·python·深度学习·计算机视觉·医学影像·特征提取