本文你将得到什么:
- 纯手工计算布林带(中轨/上轨/下轨,20日 + 2倍标准差)
- 用代码自动检测"收盘价上穿上轨"的突破点
- 用"成交量确认 + 收线位置确认"两条规则过滤假突破
- 双面板可视化(价格带通道 + 成交量)
- 可运行的完整代码
痛点:突破追进去就被套
股价冲出布林带上轨了,你追进去,第二天就回落------这就是经典的"假突破"。布林带突破策略最大的敌人不是突破本身,而是那些"摸了一下上轨就缩回去"的伪信号。
怎么区分真突破和假突破?单看价格不够,要加两个过滤器:量能 和收线位置。本文把这套逻辑写成 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 ),换证后代码无需修改。
第二步:手工计算布林带
布林带三条线的定义:
- 中轨 = N 日收盘价的移动平均(常用 N=20)
- 上轨 = 中轨 + K × N 日标准差(常用 K=2)
- 下轨 = 中轨 - 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
第四步:真假突破过滤------两条确认规则
光有"上穿"不够。给每个突破加两个问题:
- 量能确认:今天放量了吗?(当日成交量 > 5日均量)
- 收线确认:收盘价收在当日振幅的上半区吗?((收盘-最低)/(最高-最低) >= 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 股惯例配色:红涨绿跌。放量突破在图上一眼就能和缩量突破区分开。
常见坑与注意
- 标准差用总体口径(ddof=0) :pandas 的
rolling().std()默认 ddof=1(样本口径),与主流行情软件的布林带略有差异。想和软件对齐就显式传ddof=0。 - 突破方向别只看上轨:下轨跌破同样是信号(往往是恐慌出清),本文只演示上穿,逻辑对称可自行扩展。
- 带宽收窄是前置信号:Squeeze(挤压)之后常有大幅波动,但方向不定------不要把收窄直接当成上涨信号。
- 演示数据 vs 真实数据:本文用演示证书跑通逻辑,数据为确定性演示序列;换正式证书后即为真实行情,代码无需修改。
小结
本文完成了:
- 手工计算布林带(MID/UPPER/LOWER + 带宽)
- 上轨突破自动检测
- 量能 + 收线双确认的真假突破过滤
- 双面板可视化
核心观点:突破信号本身不值钱,确认条件才是策略的护城河。下一篇我们换一个视角------不看价格指标,改看成交量本身:OBV 与量价背离的 Python 实现。
免责声明
本文仅为技术教学,不构成任何投资建议。投资有风险,入市需谨慎。